Как объединить cdkVirtualScroll с cdkTable? - PullRequest
0 голосов
/ 23 февраля 2019

Я ищу рабочую виртуальную таблицу прокрутки с фиксированными заголовками, поэтому я нашел Cdk, который великолепен, но документация действительно сложна для подражания.В данный момент я пытаюсь объединить CdkTable с CdkVirtualScoll.

Все рабочие примеры, которые я смог найти, используют таблицу материалов, но я не знаю.

Итак, как мне заставить 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 голосов
/ 24 февраля 2019

Вот обновленное решение

. Большая проблема предыдущего кода заключалась в том, что он не мог динамически вычислять ширину столбцов.Поэтому необходимо было указать с каждым столбцом.

Эта версия исправляет эту проблему.

@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>

Вот и все!Не очень хорошо, но работает ...

0 голосов
/ 24 февраля 2019

Поскольку я не мог найти реально работающее решение, я написал свой «быстрый и грязный» код для фиксированного заголовка.Тем не менее я надеюсь найти гораздо лучший путь в будущем.Возможно, в следующем выпуске 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 width.В противном случае ширина th и ширина td могут различаться - что выглядит ужасно ...

Может быть, у кого-то еще есть решение сделать "настоящий" клон после того, как нарисована таблица cdk-virtual-scroll-viewport.

...