我正在寻找具有固定标题的有效虚拟滚动表,因此我发现了Cdk很好,但文档确实很难遵循。目前,我正在尝试将CdkTable
与CdkVirtualScoll
结合使用。
我能找到的所有工作示例都在使用Material表,但我没有。
那我怎样才能CdkVirtualScoll
上班呢?到目前为止,这是我所做的(来自示例):
<cdk-virtual-scroll-viewport>
<cdk-table [dataSource]="dataSource">
<ng-container cdkColumnDef="username">
<cdk-header-cell *cdkHeaderCellDef> User name </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.username}} </cdk-cell>
</ng-container>
<ng-container cdkColumnDef="title">
<cdk-header-cell *cdkHeaderCellDef> Title </cdk-header-cell>
<cdk-cell *cdkCellDef="let row"> {{row.title}} </cdk-cell>
</ng-container>
<!-- Header and Row Declarations -->
<cdk-header-row *cdkHeaderRowDef="['username', 'age']"></cdk-header-row>
<cdk-row *cdkRowDef="let row; columns: ['username', 'age']"></cdk-row>
</cdk-table>
</cdk-virtual-scroll-viewport>
在编写文档时,该表已包装到滚动视口中。但是如何设置*cdkVirtualFor
现在呢?
谢谢您的帮助!
答案 0 :(得分:0)
因为找不到真正有效的解决方案,所以我为固定的标头编写了自己的“快速且肮脏的”代码。不过,我希望将来能找到更好的方法。也许下一个版本的Cdk将提供解决方案。
我现在要做的是编写一个(或多或少的hack)指令,该指令从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
元素,但是要为th
元素正确的宽度计算出CSS样式还不知道。
因此,所有th
元素都必须具有CSS width属性。否则th
的宽度和td
的宽度可能会有所不同-看起来很丑...
在绘制cdk-virtual-scroll-viewport
表之后,也许其他人有解决方案来制作“真实的”克隆。
答案 1 :(得分:0)
这是更新的解决方案
以前的代码的一个大问题是它不能动态地计算列的宽度。因此,必须为每列指定一个with。
此版本解决了此问题。
select Name, cast(sum(A) as float)/cast(sum(B) as float) Percentage
from TableName
group by Name
用法:
用法已稍作更改,因为在处理@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
就是这样!不太好但是可以工作...