如何将cdkVirtualScroll与cdkTable结合?

时间:2019-02-23 12:19:27

标签: angular angular-cdk virtualscroll angular-cdk-virtual-scroll

我正在寻找具有固定标题的有效虚拟滚动表,因此我发现了Cdk很好,但文档确实很难遵循。目前,我正在尝试将CdkTableCdkVirtualScoll结合使用。

我能找到的所有工作示例都在使用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现在呢?

谢谢您的帮助!

2 个答案:

答案 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

就是这样!不太好但是可以工作...