Вызвать код с двумя значениями float
, код работает в любом диапазоне.
float rand_FloatRange(float a, float b)
{
return ((b - a) * ((float)rand() / RAND_MAX)) + a;
}
Поскольку я не мог найти реально работающее решение, я написал свой «быстрый и грязный» код для фиксированного заголовка. Тем не менее, я надеюсь найти гораздо лучший путь в будущем. Возможно, следующая версия Cdk предложит решение.
Теперь я написал директиву (более или менее хакерскую), которая клонирует таблицу из cdk-virtual-scroll-viewport
и размещает клонированный узел раньше. На следующем шаге элемент visibility
элемента table thead
устанавливается на collapse
.
Использование:
<cdk-virtual-scroll-viewport [itemSize]="30" cloneThead>
<table class="table table-hover">
<thead>
...
</thead>
<tbody>
<tr *cdkVirtualFor="let item of list">
<td>...</td>
...
</tr>
</tbody>
</table>
</cdk-virtual-scroll-viewport>
Директива cloneThead
довольно проста:
import { Directive, AfterViewInit, ElementRef } from '@angular/core';
@Directive({
selector: '[cloneThead]'
})
export class CloneDirective implements AfterViewInit{
constructor(private el: ElementRef) {}
ngAfterViewInit(){
let cloned = this.el.nativeElement.cloneNode(true);
let table = cloned.querySelector("table");
table.style.position = 'sticky';
table.style.top = '0';
table.style.zIndex = '100';
this.el.nativeElement.appendChild(table);
}
}
Это работает довольно хорошо, но имеет еще одну большую проблему: Клон создается после ngAfterViewInit
, что приводит к тому, что строки таблицы cdkVirtualFor
еще не созданы для DOM.
Это хорошо для самого клона, потому что он еще не содержит tr
элементов tbody
, НО вычисленные стили CSS для правильной ширины для элементов th
также не известен.
Таким образом, все элементы th
должны иметь атрибут ширины CSS. В противном случае ширина th
и td
могут различаться - что выглядит ужасно ...
Может быть, у кого-то еще есть решение сделать «настоящий» клон, после того как cdk-virtual-scroll-viewport
-таблица была нарисована.
Вот обновленное решение
. Большая проблема предыдущего кода заключалась в том, что он не мог динамически вычислять ширину столбцов. Поэтому необходимо было указать с каждым столбцом.
Эта версия исправляет эту проблему.
@Directive({
selector: '[cdkFixedHeader]'
})
export class FixedHeaderDirective implements AfterViewInit{
constructor(private el: ElementRef, private renderer:Renderer2) {}
ngAfterViewInit(){
// get the viewport element
let cdkViewport = this.el.nativeElement.closest("cdk-virtual-scroll-viewport");
// check if table was already cloned
let clonedHeader = cdkViewport.querySelectorAll('.cloned-header');
// create a clone if not exists
if (clonedHeader.length == 0)
{
let table = this.el.nativeElement.closest('table');
let cloned = table.cloneNode(true);
cloned.style.position = 'sticky';
cloned.style.top = '0';
cloned.style.zIndex = '100';
// remove tbody with elements
let tbody = cloned.querySelector('tbody');
cloned.removeChild(tbody);
// add a "helper" class
this.renderer.addClass(cloned, "cloned-header");
// append cloned object to viewport
cdkViewport.appendChild(cloned);
}
//
// walk through all <tr> with their <td> and store the max value in an array
//
let width = [];
let td = this.el.nativeElement.querySelectorAll("td");
width = new Array(td.length).fill(0);
td.forEach((item,index) => {
const w = item.getBoundingClientRect().width;
width[index] = Math.max(w, width[index]);
})
//
// get <th> elements and apply the max-width values
//
let th = cdkViewport.querySelectorAll('.cloned-header th');
th.forEach((item,index) => {
this.renderer.setStyle(item, "min-width", width[index] + 'px')
})
}
}
Использование:
Использование немного изменилось, потому что это было необходимо для вызова директивы, когда обрабатывается *cdkVirtualFor
.
<tr *cdkVirtualFor="let item of list" cdkFixedHeader>
...
</tr>
Вот и все! Не очень хорошо, но работает ...