Как совместить cdkVirtualScroll с cdkTable?

Я ищу работающую виртуальную таблицу прокрутки с фиксированными заголовками, поэтому я нашел 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сейчас?

Спасибо за вашу помощь!

Взгляните на это обсуждение, возможно, оно поможет ссылка на сайт

Amir Arbabian 23.02.2019 13:42

@AmirArbabian: я уже сделал и потратил час работы, чтобы заставить его работать, но это не так. Разве нет действительно повторного использования примера с простым кодом?

Lars 23.02.2019 14:13

Попробуйте, например, Вот этот.

Amir Arbabian 23.02.2019 14:25

@AmirArbabian: извините, мой ответ был неправильным: я запустил код (как в примере), но проблема по-прежнему в том, что заголовок НЕ фиксирован, а перемещается.

Lars 23.02.2019 14:31

Ларс Хаген. Привет. там, как я могу связаться с вами? Мне нужна помощь с cdk-virtual-scroll.

mx_code 08.06.2020 10:49

@mex Я создал чат чат.stackoverflow.com/rooms/215503/мекс

Lars 08.06.2020 10:55

Спасибо, Ларс. Он работает идеально. Спасибо за уделенное время.

mx_code 08.06.2020 11:49

Привет. Ларс. Мне нужна помощь с ngx-virtual scroll. Как я могу добавить обычную горизонтальную полосу прокрутки для таблицы с ngx-virtual scroll, если происходит переполнение в направлении x?

mx_code 10.06.2020 19:32
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
3
8
3 436
2

Ответы 2

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

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

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

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

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

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

Другие вопросы по теме