angular 9 настраиваемая таблица с разбивкой по страницам, фильтром, проблемой производительности сортировки - PullRequest
0 голосов
/ 12 апреля 2020

Мне нужна была одна таблица с такими функциями, как нумерация страниц, фильтр (каждый столбец), сортировка, складная строка, редактируемая строка, фиксированная общая строка внизу, но я не смог найти точный плагин. поэтому я пытаюсь создать свой собственный плагин. я придумал нумерацию страниц, фильтр и сортировку на данный момент. и он работает, как ожидалось, но он загружает слишком много времени. на нумерации страниц 10 это работает нормально, но для 50 это занимает слишком много времени.

m-table.component. html

<div class="datatable table-responsive">
    <div class="row mb-4">
        <div class="col-sm-12" style="overflow-x: scroll;">
            <table class="table table-bordered table-hover mb-0" width="100%" cellspacing="0">
                <thead>
                    <tr>
                        <th *ngFor="let header of headers; let i = index;">
                            <a href="javascript:void(0);" (click)="sort(header)" style="text-decoration: none;">{{settings.columns[header]['title']}}</a>

                            <img class="float-right" src="../../../assets/img/sort-up.webp" *ngIf="sortHeader === header && direction" style="width: 18px;" />
                            <img class="float-right" src="../../../assets/img/sort-down.webp" *ngIf="sortHeader === header && !direction" style="width: 18px;" />
                        </th>
                    </tr>
                </thead>
                <tfoot>
                    <tr>
                        <th *ngFor="let header of headers">{{settings.columns[header]['title']}}</th>
                    </tr>
                </tfoot>
                <tbody>

                    <tr>
                        <td *ngFor="let header of headers">
                            <input class="form-control form-control-sm" [(ngModel)]="settings.columns[header]['filter']"
                            (ngModelChange)="filterChangeEvent($event, header)"
                            placeholder="{{settings.columns[header]['title']}}"/>
                        </td>
                    </tr>

                    <tr *ngFor="let record of pageOfItems">
                        <td *ngFor="let header of headers">
                            <div *ngIf="!settings.columns[header].type" [ngClass]="settings.columns[header].pill ? 'badge badge-primary badge-pill' : ''">
                                {{record[header]}}
                            </div>
                            <div *ngIf="settings.columns[header].type === 'date'" [ngClass]="settings.columns[header].pill ? 'badge badge-primary badge-pill' : ''">
                                {{record[header] | date: 'yyyy-mm-dd hh:mm:ss'}}
                            </div>
                        </td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
    <div class="row">
        <div class="col-sm-12">
            <div class="px-4">
                <app-pagination [items]="dataCopy" [settings]="settings.pager"  (changePage)="onChangePage($event)"></app-pagination>
            </div>
        </div>
    </div>
</div>

m- table.component.ts

export class MTableComponent implements OnInit {
    filterQuery: string;
    filterQueryChanged: Subject<string> = new Subject<string>();

    @Input() data;
    @Input() settings;

    dataCopy = [];
    pageOfItems: Array<any>;
    siteName = '';
    headers = [];
    direction = false;
    sortHeader = '';

    constructor() {

        this.filterQueryChanged.pipe(debounceTime(2000), distinctUntilChanged()).subscribe(query => {
            this.filterQuery = query;
            this.dataCopy = JSON.parse(JSON.stringify(this.data));
            for(let key in this.settings.columns) {
                if(this.settings.columns[key]['filter'] && this.settings.columns[key]['filter'] != '') {
                    this.dataCopy = this.dataCopy.filter(a => a[key].toLowerCase().includes(this.settings.columns[key]['filter'].toLowerCase()));
                }
            }
        });

    }

    ngOnInit(): void {}

    ngOnChanges() {
        this.dataCopy = JSON.parse(JSON.stringify(this.data));
        this.headers = Object.keys(this.settings.columns);
    }

    filterChangeEvent(search, column) {
        this.filterQueryChanged.next(search);
    }

    compareValues(direction: any, a: any, b: any) {
        if (a < b) {
            return -1 * direction;
        }
        if (a > b) {
            return direction;
        }
        return 0;
    }

    sort(header: string) {
        if(this.sortHeader === header) {
            this.direction = !this.direction;
        } else {
            this.direction = false;
        }
        this.sortHeader = header;
        const dir: number = this.direction ? 1 : -1;
        const compare: Function = this.compareValues;
        let dataCopy = this.dataCopy.sort((a, b) => {
            return compare.call(null, dir, a[header], b[header]);
        });
        // did this because pagination was not detecting changes
        this.dataCopy = JSON.parse(JSON.stringify(dataCopy));
    }

    onChangePage(pageOfItems: Array<any>) {
        this.pageOfItems = pageOfItems;
    }
}

pagination.component. html

<div class="dataTables_paginate paging_simple_numbers" *ngIf="pager.pages && pager.pages.length">
    <div class="float-left">
        <div>
            <span class="float-left" style="padding-top: 7px;">Showing {{pager.startIndex + 1}} to {{pager.endIndex + 1}} of {{pager.totalItems}} entries</span>
        </div>
    </div>

    <div class="float-right">
        <ul class="pagination">
            <li [ngClass]="{disabled:pager.currentPage === 1}" class="page-item first-item">
                <a (click)="setPage(1)" class="page-link">
                    <i class="fas fa-angle-double-left"></i>
                </a>
            </li>
            <li [ngClass]="{disabled:pager.currentPage === 1}" class="page-item previous-item">
                <a (click)="setPage(pager.currentPage - 1)" class="page-link">
                    <i class="fas fa-angle-left"></i>
                </a>
            </li>
            <li *ngFor="let page of pager.pages" [ngClass]="{active:pager.currentPage === page}" class="page-item number-item">
                <a (click)="setPage(page)" class="page-link">{{page}}</a>
            </li>
            <li [ngClass]="{disabled:pager.currentPage === pager.totalPages}" class="page-item next-item">
                <a (click)="setPage(pager.currentPage + 1)" class="page-link">
                    <i class="fas fa-angle-right"></i>
                </a>
            </li>
            <li [ngClass]="{disabled:pager.currentPage === pager.totalPages}" class="page-item last-item">
                <a (click)="setPage(pager.totalPages)" class="page-link">
                    <i class="fas fa-angle-double-right"></i>
                </a>
            </li>
        </ul>
    </div>

    <div class="float-right mr-3" style="margin-top: 3px;">
        <select [(ngModel)]="settings.pageSize" (ngModelChange)="setPage(initialPage)" class="form-control form-control-sm" style="width: 160px;">
            <option *ngFor="let pageSize of settings.pageSizeOptions" [value]="pageSize">{{pageSize}}</option>
        </select>
    </div>

</div>

pagination.component.ts

export class PaginationComponent implements OnInit {

    @Input() items: Array<any>;
    @Output() changePage = new EventEmitter<any>(true);
    @Input() initialPage = 1;
    @Input() settings = {
        pageSize: 100,
        pageSizeOptions: [100, 200, 500],
        maxPages: 5
    };

    pager: any = {};

    ngOnInit() {
        if (this.items && this.items.length) {
            this.setPage(this.initialPage);
        }
    }

    ngOnChanges(changes: SimpleChanges) {
        if (changes.items.currentValue !== changes.items.previousValue) {
            this.setPage(this.initialPage);
        }
    }

    setPage(page: number) {
        this.pager = paginate(this.items.length, page, this.settings.pageSize, this.settings.maxPages);
        var pageOfItems = this.items.slice(this.pager.startIndex, this.pager.endIndex + 1);
        this.changePage.emit(pageOfItems);
    }
}

paginate.ts

export default function paginate(
totalItems: number,
currentPage: number = 1,
pageSize: number = 10,
maxPages: number = 10
) {
    let totalPages = Math.ceil(totalItems / pageSize);
    if (currentPage < 1) {
        currentPage = 1;
    } else if (currentPage > totalPages) {
        currentPage = totalPages;
    }

    let startPage: number, endPage: number;
    if (totalPages <= maxPages) {
        startPage = 1;
        endPage = totalPages;
    } else {
        let maxPagesBeforeCurrentPage = Math.floor(maxPages / 2);
        let maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1;
        if (currentPage <= maxPagesBeforeCurrentPage) {
            startPage = 1;
            endPage = maxPages;
        } else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
            startPage = totalPages - maxPages + 1;
            endPage = totalPages;
        } else {
            startPage = currentPage - maxPagesBeforeCurrentPage;
            endPage = currentPage + maxPagesAfterCurrentPage;
        }
    }

    let startIndex = (currentPage - 1) * pageSize;
    let endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

    let pages = Array.from(Array((endPage + 1) - startPage).keys()).map(i => startPage + i);

    return {
        totalItems: totalItems,
        currentPage: currentPage,
        pageSize: pageSize,
        totalPages: totalPages,
        startPage: startPage,
        endPage: endPage,
        startIndex: startIndex,
        endIndex: endIndex,
        pages: pages
    };
}

Я не могу понять, что не так и как это исправить. все будет полезно, спасибо.

демо stackblitz

1 Ответ

0 голосов
/ 13 апреля 2020

Честно говоря, у меня нет проблем с производительностью на моем компьютере. Однако, как правило, причиной плохой производительности таблиц является тот факт, что вы должны визуализировать много DOM-контента на своей странице. Чтобы оптимизировать его, вы можете реализовать функцию virtual-scroll для вашей таблицы. Или используйте одну из этих таблиц, в которой она уже есть:

1) https://material.angular.io/components/table/overview

2) https://github.com/swimlane/ngx-datatable

...