From 0e16e30b397a03df896858c7b9cfe16528292a3a Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Thu, 1 Aug 2024 11:26:07 +0300 Subject: [PATCH 1/7] feat(grid): optimize grid row selection flow and performance --- .../lib/grids/selection/selection.service.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index 1d6e92e8280..9aa937f08ae 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -432,11 +432,13 @@ export class IgxGridSelectionService { /** Select all rows, if filtering is applied select only from filtered data. */ public selectAllRows(event?) { - const addedRows = this.allData.filter((row) => !this.isRowSelected(this.getRecordKey(row))); + const allData = this.allData; + const allDataKeys = new Set(allData.map(row => this.getRecordKey(row))); + const addedRows = allData.filter((row) => !this.rowSelection.has(this.getRecordKey(row))); const selectedRows = this.getSelectedRowsData(); - const newSelection = this.rowSelection.size ? selectedRows.concat(addedRows) : addedRows; + const newSelection = addedRows; this.indeterminateRows.clear(); - this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows); + this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows, allDataKeys); } /** Select the specified row and emit event. */ @@ -610,9 +612,7 @@ export class IgxGridSelectionService { return this.allRowsSelected; } const selectedData = new Set(newSelection ? newSelection : [...this.rowSelection]); - const allData = this.getRowIDs(this.allData); - const unSelectedRows = allData.filter(row => !selectedData.has(row)); - return this.allRowsSelected = this.allData.length > 0 && unSelectedRows.length === 0; + return this.allRowsSelected = this.allData.length > 0 && selectedData.size === this.allData.length; } public hasSomeRowSelected(): boolean { @@ -627,7 +627,7 @@ export class IgxGridSelectionService { this.getSelectedRows().filter(rowID => !this.isRowDeleted(rowID)); } - public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?): boolean { + public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?, allDataKeys?): boolean { currSelection = currSelection ?? this.getSelectedRowsData(); if (this.areEqualCollections(currSelection, newSelection)) { return; @@ -638,7 +638,9 @@ export class IgxGridSelectionService { newSelection, added, removed, event, cancel: false, - allRowsSelected: this.areAllRowSelected(newSelection.map(r => this.getRecordKey(r))) + allRowsSelected: allDataKeys ? + newSelection.length === allDataKeys.size : + this.areAllRowSelected(newSelection.map(r => this.getRecordKey(r))) }; this.grid.rowSelectionChanging.emit(args); From b16204f69b461f6ed4e37cd8f42a23e26c977e9c Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Thu, 1 Aug 2024 12:07:15 +0300 Subject: [PATCH 2/7] feat(grid): optimize row selection and fix filtering and deletion tests --- .../src/lib/grids/selection/selection.service.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index 9aa937f08ae..1707692b90a 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -432,9 +432,10 @@ export class IgxGridSelectionService { /** Select all rows, if filtering is applied select only from filtered data. */ public selectAllRows(event?) { - const allData = this.allData; - const allDataKeys = new Set(allData.map(row => this.getRecordKey(row))); - const addedRows = allData.filter((row) => !this.rowSelection.has(this.getRecordKey(row))); + const relevantData = this.isFilteringApplied() ? this.grid.filteredData : this.allData; + const nonDeletedData = relevantData.filter(row => !this.isRowDeleted(this.getRecordKey(row))); + const allDataKeys = new Set(nonDeletedData.map(row => this.getRecordKey(row))); + const addedRows = nonDeletedData.filter((row) => !this.rowSelection.has(this.getRecordKey(row))); const selectedRows = this.getSelectedRowsData(); const newSelection = addedRows; this.indeterminateRows.clear(); @@ -612,7 +613,10 @@ export class IgxGridSelectionService { return this.allRowsSelected; } const selectedData = new Set(newSelection ? newSelection : [...this.rowSelection]); - return this.allRowsSelected = this.allData.length > 0 && selectedData.size === this.allData.length; + const relevantData = this.isFilteringApplied() ? this.grid.filteredData : this.allData; + const nonDeletedData = relevantData.filter(row => !this.isRowDeleted(this.getRecordKey(row))); + return this.allRowsSelected = nonDeletedData.length > 0 && + selectedData.size === nonDeletedData.length; } public hasSomeRowSelected(): boolean { From 3ee77648cf58185ca6f9c166163245ed1ace00a2 Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Thu, 1 Aug 2024 13:41:45 +0300 Subject: [PATCH 3/7] feat(grid): refactor row selection methods for improved clarity and performance --- .../lib/grids/selection/selection.service.ts | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index 1707692b90a..be06c05a626 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -432,14 +432,12 @@ export class IgxGridSelectionService { /** Select all rows, if filtering is applied select only from filtered data. */ public selectAllRows(event?) { - const relevantData = this.isFilteringApplied() ? this.grid.filteredData : this.allData; - const nonDeletedData = relevantData.filter(row => !this.isRowDeleted(this.getRecordKey(row))); - const allDataKeys = new Set(nonDeletedData.map(row => this.getRecordKey(row))); - const addedRows = nonDeletedData.filter((row) => !this.rowSelection.has(this.getRecordKey(row))); + const allDataKeys = this.allData.map(row => this.getRecordKey(row)); + const addedRows = this.allData.filter((row, index) => !this.isRowSelected(allDataKeys[index])); const selectedRows = this.getSelectedRowsData(); - const newSelection = addedRows; + const newSelection = this.rowSelection.size ? selectedRows.concat(addedRows) : addedRows; this.indeterminateRows.clear(); - this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows, allDataKeys); + this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows); } /** Select the specified row and emit event. */ @@ -612,11 +610,10 @@ export class IgxGridSelectionService { if (this.allRowsSelected !== undefined && !newSelection) { return this.allRowsSelected; } - const selectedData = new Set(newSelection ? newSelection : [...this.rowSelection]); + const selectedData = new Set(newSelection ? newSelection.map(r => this.getRecordKey(r)) : [...this.rowSelection]); const relevantData = this.isFilteringApplied() ? this.grid.filteredData : this.allData; - const nonDeletedData = relevantData.filter(row => !this.isRowDeleted(this.getRecordKey(row))); - return this.allRowsSelected = nonDeletedData.length > 0 && - selectedData.size === nonDeletedData.length; + const unSelectedRows = relevantData.filter(row => !selectedData.has(this.getRecordKey(row)) && !this.isRowDeleted(this.getRecordKey(row))); + return this.allRowsSelected = relevantData.length > 0 && unSelectedRows.length === 0; } public hasSomeRowSelected(): boolean { @@ -631,7 +628,7 @@ export class IgxGridSelectionService { this.getSelectedRows().filter(rowID => !this.isRowDeleted(rowID)); } - public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?, allDataKeys?): boolean { + public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?): boolean { currSelection = currSelection ?? this.getSelectedRowsData(); if (this.areEqualCollections(currSelection, newSelection)) { return; @@ -640,11 +637,11 @@ export class IgxGridSelectionService { owner: this.grid, oldSelection: currSelection, newSelection, - added, removed, - event, cancel: false, - allRowsSelected: allDataKeys ? - newSelection.length === allDataKeys.size : - this.areAllRowSelected(newSelection.map(r => this.getRecordKey(r))) + added, + removed, + event, + cancel: false, + allRowsSelected: this.areAllRowSelected(newSelection) }; this.grid.rowSelectionChanging.emit(args); From aa815b039e7e7a26b82884798be557fb72b03911 Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Thu, 1 Aug 2024 15:19:18 +0300 Subject: [PATCH 4/7] feat(grid): refine row selection logic --- .../src/lib/grids/selection/selection.service.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index be06c05a626..bb7c84cca26 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -611,9 +611,8 @@ export class IgxGridSelectionService { return this.allRowsSelected; } const selectedData = new Set(newSelection ? newSelection.map(r => this.getRecordKey(r)) : [...this.rowSelection]); - const relevantData = this.isFilteringApplied() ? this.grid.filteredData : this.allData; - const unSelectedRows = relevantData.filter(row => !selectedData.has(this.getRecordKey(row)) && !this.isRowDeleted(this.getRecordKey(row))); - return this.allRowsSelected = relevantData.length > 0 && unSelectedRows.length === 0; + const unSelectedRows = this.allData.filter(row => !selectedData.has(this.getRecordKey(row)) && !this.isRowDeleted(this.getRecordKey(row))); + return this.allRowsSelected = this.allData.length > 0 && unSelectedRows.length === 0; } public hasSomeRowSelected(): boolean { From 867027e7f461a8f9173537d0a21b98a31c024760 Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Wed, 11 Sep 2024 09:32:37 +0300 Subject: [PATCH 5/7] feat(grid): optimize row selection flow to avoid redundant operations --- .../lib/grids/selection/selection.service.ts | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index bb7c84cca26..814caa70f63 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -432,12 +432,11 @@ export class IgxGridSelectionService { /** Select all rows, if filtering is applied select only from filtered data. */ public selectAllRows(event?) { - const allDataKeys = this.allData.map(row => this.getRecordKey(row)); - const addedRows = this.allData.filter((row, index) => !this.isRowSelected(allDataKeys[index])); + const addedRows = this.allData.filter((row) => !this.rowSelection.has(this.getRecordKey(row))); const selectedRows = this.getSelectedRowsData(); const newSelection = this.rowSelection.size ? selectedRows.concat(addedRows) : addedRows; this.indeterminateRows.clear(); - this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows); + this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows, this.allData); } /** Select the specified row and emit event. */ @@ -519,18 +518,17 @@ export class IgxGridSelectionService { return; } - let rowsToSelect = keys.filter(x => !this.isRowDeleted(x) && !this.rowSelection.has(x)); - if (!rowsToSelect.length && !clearPrevSelection) { - // no valid/additional rows to select and no clear - return; - } - + const allData = this.allData; + const allDataMap = new Map(allData.map(row => [this.getRecordKey(row), row])); + const rowsToSelect = keys.filter(key => !this.isRowDeleted(key) && !this.rowSelection.has(key)); const selectedRows = this.getSelectedRowsData(); - rowsToSelect = this.grid.primaryKey ? rowsToSelect.map(r => this.getRowDataById(r)) : rowsToSelect; - const newSelection = clearPrevSelection ? rowsToSelect : [...selectedRows, ...rowsToSelect]; - const keysAsSet = new Set(rowsToSelect); - const removed = clearPrevSelection ? selectedRows.filter(x => !keysAsSet.has(x)) : []; - this.emitRowSelectionEvent(newSelection, rowsToSelect, removed, event, selectedRows); + + const newSelection = clearPrevSelection ? + rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean) : + [...selectedRows, ...rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean)]; + + const removed = clearPrevSelection ? selectedRows.filter(row => !keys.includes(this.getRecordKey(row))) : []; + this.emitRowSelectionEvent(newSelection, rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean), removed, event, selectedRows, allData); } public deselectRows(keys: any[], event?): void { @@ -603,16 +601,16 @@ export class IgxGridSelectionService { this.emitRowSelectionEvent(newSelection, added, [], event, currSelection); } - public areAllRowSelected(newSelection?): boolean { + public areAllRowSelected(newSelection?, allData?): boolean { if (!this.grid.data && !newSelection) { return false; } if (this.allRowsSelected !== undefined && !newSelection) { return this.allRowsSelected; } + allData = allData || this.allData; const selectedData = new Set(newSelection ? newSelection.map(r => this.getRecordKey(r)) : [...this.rowSelection]); - const unSelectedRows = this.allData.filter(row => !selectedData.has(this.getRecordKey(row)) && !this.isRowDeleted(this.getRecordKey(row))); - return this.allRowsSelected = this.allData.length > 0 && unSelectedRows.length === 0; + return this.allRowsSelected = allData.length > 0 && allData.every(row => selectedData.has(this.getRecordKey(row))); } public hasSomeRowSelected(): boolean { @@ -627,20 +625,19 @@ export class IgxGridSelectionService { this.getSelectedRows().filter(rowID => !this.isRowDeleted(rowID)); } - public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?): boolean { + public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?, allData?): boolean { currSelection = currSelection ?? this.getSelectedRowsData(); if (this.areEqualCollections(currSelection, newSelection)) { return; } + allData = allData || this.allData; const args: IRowSelectionEventArgs = { owner: this.grid, oldSelection: currSelection, newSelection, - added, - removed, - event, - cancel: false, - allRowsSelected: this.areAllRowSelected(newSelection) + added, removed, + event, cancel: false, + allRowsSelected: this.areAllRowSelected(newSelection, allData) }; this.grid.rowSelectionChanging.emit(args); From e5ce55ad4fa0d2f7c27060aaf9cd9656aa6ca91b Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Wed, 11 Sep 2024 10:01:43 +0300 Subject: [PATCH 6/7] chore(grid): undoing recent unnecessary leftover changes --- .../lib/grids/selection/selection.service.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index 814caa70f63..c1ed243c82a 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -518,17 +518,18 @@ export class IgxGridSelectionService { return; } - const allData = this.allData; - const allDataMap = new Map(allData.map(row => [this.getRecordKey(row), row])); - const rowsToSelect = keys.filter(key => !this.isRowDeleted(key) && !this.rowSelection.has(key)); - const selectedRows = this.getSelectedRowsData(); - - const newSelection = clearPrevSelection ? - rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean) : - [...selectedRows, ...rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean)]; + let rowsToSelect = keys.filter(x => !this.isRowDeleted(x) && !this.rowSelection.has(x)); + if (!rowsToSelect.length && !clearPrevSelection) { + // no valid/additional rows to select and no clear + return; + } - const removed = clearPrevSelection ? selectedRows.filter(row => !keys.includes(this.getRecordKey(row))) : []; - this.emitRowSelectionEvent(newSelection, rowsToSelect.map(key => allDataMap.get(key)).filter(Boolean), removed, event, selectedRows, allData); + const selectedRows = this.getSelectedRowsData(); + rowsToSelect = this.grid.primaryKey ? rowsToSelect.map(r => this.getRowDataById(r)) : rowsToSelect; + const newSelection = clearPrevSelection ? rowsToSelect : [...selectedRows, ...rowsToSelect]; + const keysAsSet = new Set(rowsToSelect); + const removed = clearPrevSelection ? selectedRows.filter(x => !keysAsSet.has(x)) : []; + this.emitRowSelectionEvent(newSelection, rowsToSelect, removed, event, selectedRows); } public deselectRows(keys: any[], event?): void { From 97b93baf2c1a3dbcb17e96588c3692e557a3db38 Mon Sep 17 00:00:00 2001 From: Georgi Anastasov Date: Fri, 20 Sep 2024 11:08:03 +0300 Subject: [PATCH 7/7] feat(grid): improve row selection handling --- .../lib/grids/selection/selection.service.ts | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts index c1ed243c82a..d17cd93480d 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -436,7 +436,7 @@ export class IgxGridSelectionService { const selectedRows = this.getSelectedRowsData(); const newSelection = this.rowSelection.size ? selectedRows.concat(addedRows) : addedRows; this.indeterminateRows.clear(); - this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows, this.allData); + this.emitRowSelectionEvent(newSelection, addedRows, [], event, selectedRows); } /** Select the specified row and emit event. */ @@ -602,16 +602,15 @@ export class IgxGridSelectionService { this.emitRowSelectionEvent(newSelection, added, [], event, currSelection); } - public areAllRowSelected(newSelection?, allData?): boolean { + public areAllRowSelected(newSelection?): boolean { if (!this.grid.data && !newSelection) { return false; } if (this.allRowsSelected !== undefined && !newSelection) { return this.allRowsSelected; } - allData = allData || this.allData; - const selectedData = new Set(newSelection ? newSelection.map(r => this.getRecordKey(r)) : [...this.rowSelection]); - return this.allRowsSelected = allData.length > 0 && allData.every(row => selectedData.has(this.getRecordKey(row))); + const selectedData = new Set(this.getRowIDs(newSelection || this.rowSelection)); + return this.allRowsSelected = this.allData.length > 0 && this.allData.every(row => selectedData.has(this.getRecordKey(row))); } public hasSomeRowSelected(): boolean { @@ -626,19 +625,21 @@ export class IgxGridSelectionService { this.getSelectedRows().filter(rowID => !this.isRowDeleted(rowID)); } - public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?, allData?): boolean { + public emitRowSelectionEvent(newSelection, added, removed, event?, currSelection?): boolean { currSelection = currSelection ?? this.getSelectedRowsData(); if (this.areEqualCollections(currSelection, newSelection)) { return; } - allData = allData || this.allData; + const args: IRowSelectionEventArgs = { owner: this.grid, oldSelection: currSelection, newSelection, - added, removed, - event, cancel: false, - allRowsSelected: this.areAllRowSelected(newSelection, allData) + added, + removed, + event, + cancel: false, + allRowsSelected: this.areAllRowSelected(newSelection) }; this.grid.rowSelectionChanging.emit(args);