javascriptangularprimengprimeng-turbotableprototype-chain

PrimeNG p-table header select all persistence with lazy loading and pagination


Current config (cannot update it to latest):
"@angular/cli": "^7.3.9", "primeng": "7.0.5",

I have a PrimeNG p-table that has lazy loaded data with pagination.
There is an issue open for it on PrimeNG GitHub too - https://github.com/primefaces/primeng/issues/8139
Stackblitz link is already attached in that issue so didn't create a new one.

Scenario:
One 1st page, some rows are selected via checkbox selection.
On 2nd page, Select All checkbox from the header is selected and all rows on 2nd page is auto-selected.
Now when navigated to the first page, the selections from here are reset. But the Select All checkbox in the header is still checked.

Would like to know if anyone has a workaround for this issue?

Any help is appreciated.

Edit: Solution found in another similar GitHub issue: https://github.com/primefaces/primeng/issues/6482

Solution: https://github.com/primefaces/primeng/issues/6482#issuecomment-456644912

Can someone help with the implementation of the override in an Angular 7/8 application. Not able to understand as how to get the TableHeaderCheckbox reference and override the prototype.


Solution

  • Well, the solution to the problem is still not added to the PrimeNG repo and so even the latest package does not have it solved.

    For time being, use the solution mentioned in the question under Edit

    To answer the question that I have asked under the Edit, check below:

    // In some service file:
    
    import { Table, TableHeaderCheckbox } from 'primeng/table';
    import { ObjectUtils } from 'primeng/components/utils/objectutils';
    import { uniq, each, intersection, map, remove } from 'lodash';
    
    @Injectable()
    export class BulkSelectAllPagesService {
    
    overridePrimeNGTableMethods() {
        TableHeaderCheckbox.prototype.updateCheckedState = function () {
            const currentRows = map(this.dt.value, this.dt.dataKey);
            const selectedRows = map(this.dt.selection, this.dt.dataKey);
            this.rowsPerPageValue = this.dt.rows;
            const commonRows = intersection(currentRows, selectedRows);
            return commonRows.length === currentRows.length;
        };
    
        Table.prototype.toggleRowsWithCheckbox = function (event, check) {
            let _selection;
            if (!check) {
                _selection = this.value.slice();
                each(_selection, (row) => {
                    const match = {}; match[this.dataKey] = row[this.dataKey];
                    remove(this._selection, match);
                });
            } else {
                _selection = check ? this.filteredValue ? this.filteredValue.slice() : this.value.slice() : [];
                each(this._selection, (row) => {
                    const match = {}; match[this.dataKey] = row[this.dataKey];
                    remove(_selection, match);
                });
                this._selection = this._selection.concat(_selection);
            }
    
            this.preventSelectionSetterPropagation = true;
            this.updateSelectionKeys();
            this.selectionChange.emit(this._selection);
            this.tableService.onSelectionChange();
            this.onHeaderCheckboxToggle.emit({
                originalEvent: event,
                affectedRows: _selection,
                checked: check
            });
        };
    }
    
    // In app.component.ts
    
    import { Component, OnInit } from '@angular/core';
    import { BulkSelectAllPagesService } from 'PATH_TO_THE_FILE/bulk-select-all-pages.service';
    
    @Component({
        selector: 'app-root',
        templateUrl: './app.component.html'
    })
    export class AppComponent implements OnInit {
    
        constructor(
            private bulkSelectAllPagesService: BulkSelectAllPagesService) {
    
        }
    
        ngOnInit() {
            this.bulkSelectAllPagesService.overridePrimeNGTableMethods();
        }
    }
    

    Ofcourse need to include the service file in the providers[] in the app.module.ts

    Will create a stackblitz and add later.

    Improved version to handle rowspan grouped data:

    overridePrimeNGTableMethods() {
        TableHeaderCheckbox.prototype.updateCheckedState = function () {
            const currentRows = map(this.dt.value, this.dt.dataKey);
            const uniqueCurrentRows = uniq(currentRows);
            const selectedRows = map(this.dt.selection, this.dt.dataKey);
            this.rowsPerPageValue = this.dt.rows;
            const commonRows = intersection(currentRows, selectedRows);
            if (currentRows.length) {
                return commonRows.length === uniqueCurrentRows.length;
            } else {
                return false;
            }
        };
    
        Table.prototype.toggleRowWithCheckbox = function (event, rowData) {
            const findIndexesInSelection = (selection: any = [], data: any = {}, dataKey: any) => {
                const indexes = [];
                if (selection && selection.length) {
                    selection.forEach((sel: any, i: number) => {
                        if (data[dataKey] === sel[dataKey]) {
                            indexes.push(i);
                        }
                    });
                }
                return indexes;
            };
    
            this.selection = this.selection || [];
            const selected = this.isSelected(rowData);
            const dataKeyValue = this.dataKey ? String(ObjectUtils.resolveFieldData(rowData, this.dataKey)) : null;
            this.preventSelectionSetterPropagation = true;
            if (selected) {
                const selectionIndexes = findIndexesInSelection(this.selection, rowData, this.dataKey);
                const selectedItems = this.selection.filter((val: any) => {
                    return val[this.dataKey] === rowData[this.dataKey];
                });
                this._selection = this.selection.filter((val: any, i: number) => {
                    return selectionIndexes.indexOf(i) === -1;
                });
                this.selectionChange.emit(this.selection);
                selectedItems.forEach((selectedItem: any, index: number) => {
                    this.onRowUnselect.emit({ originalEvent: event.originalEvent, index: event.rowIndex + index, data: selectedItem, type: 'checkbox' });
                });
                delete this.selectionKeys[rowData[this.dataKey]];
            } else {
                let rows = [rowData];
                if (dataKeyValue) {
                    rows = this.value.filter(val => {
                        return (val[this.dataKey]).toString() === dataKeyValue;
                    });
                }
                this._selection = this.selection ? this.selection.concat(rows) : rows;
                this.selectionChange.emit(this.selection);
                this.onRowSelect.emit({ originalEvent: event.originalEvent, index: event.rowIndex, data: rowData, type: 'checkbox' });
                if (dataKeyValue) {
                    this.selectionKeys[dataKeyValue] = 1;
                }
            }
            this.tableService.onSelectionChange();
            if (this.isStateful()) {
                this.saveState();
            }
        };
    
        Table.prototype.toggleRowsWithCheckbox = function (event, check) {
            let _selection;
            if (!check) {
                _selection = this.value.slice();
                each(_selection, (row) => {
                    const match = {}; match[this.dataKey] = row[this.dataKey];
                    remove(this._selection, match);
                });
            } else {
                _selection = check ? this.filteredValue ? this.filteredValue.slice() : this.value.slice() : [];
                each(this._selection, (row) => {
                    const match = {}; match[this.dataKey] = row[this.dataKey];
                    remove(_selection, match);
                });
                this._selection = this._selection.concat(_selection);
            }
    
            this.preventSelectionSetterPropagation = true;
            this.updateSelectionKeys();
            this.selectionChange.emit(this._selection);
            this.tableService.onSelectionChange();
            this.onHeaderCheckboxToggle.emit({
                originalEvent: event,
                affectedRows: _selection,
                checked: check
            });
        };
    }