From d3f3abcf5953559eb973e1d71e65a5a0bf696dbd Mon Sep 17 00:00:00 2001 From: Stamen Stoychev Date: Fri, 9 Jan 2026 17:15:59 +0200 Subject: [PATCH] refactor(selection): use objects instead of row indexes for selection map --- .../src/lib/grids/grid-base.directive.ts | 21 ++++--- .../lib/grids/selection/selection.service.ts | 61 ++++++++++++++----- 2 files changed, 58 insertions(+), 24 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts index 4174cc4ffa8..4a347395933 100644 --- a/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts +++ b/projects/igniteui-angular/src/lib/grids/grid-base.directive.ts @@ -4108,7 +4108,7 @@ export abstract class IgxGridBaseDirective implements GridType, const activeRow = this.navigation.activeNode?.row; const selectedCellIndexes = this.selectionService.selection - ? Array.from(this.selectionService.selection.keys()) + ? this.selectionService.getSelectedCellRowIndexes() : []; this._activeRowIndexes = [activeRow, ...selectedCellIndexes]; return this._activeRowIndexes; @@ -7485,15 +7485,16 @@ export abstract class IgxGridBaseDirective implements GridType, } if (this.selectionService.selection.size > 0) { if (expansionRowIndexes.length > 0) { - for (const [key, value] of this.selectionService.selection.entries()) { - const updatedKey = key; + for (const [rowObject, value] of this.selectionService.selection.entries()) { + const rowIndex = this.selectionService.getRowIndexByObject(rowObject); + if (rowIndex === -1) continue; let subtract = 0; expansionRowIndexes.forEach((row) => { - if (updatedKey > Number(row)) { + if (rowIndex > Number(row)) { subtract++; } }); - selectionCollection.set(updatedKey - subtract, value); + selectionCollection.set(rowIndex - subtract, value); } } } else if (activeEl) { @@ -7511,13 +7512,17 @@ export abstract class IgxGridBaseDirective implements GridType, const totalItems = (this as any).totalItemCount ?? 0; const isRemote = totalItems && totalItems > this.dataView.length; - let selectionMap; + let selectionMap: [number, Set][]; if (this.type === 'hierarchical' && selectionCollection.size > 0) { selectionMap = isRemote ? Array.from(selectionCollection) : Array.from(selectionCollection).filter((tuple) => tuple[0] < source.length); } else { - selectionMap = isRemote ? Array.from(this.selectionService.selection) : - Array.from(this.selectionService.selection).filter((tuple) => tuple[0] < source.length); + // Convert row objects to row indexes for selection map + const selectionWithIndexes: [number, Set][] = Array.from(this.selectionService.selection.entries()) + .map(([rowObject, columns]) => [this.selectionService.getRowIndexByObject(rowObject), columns] as [number, Set]) + .filter(([rowIndex]) => rowIndex !== -1); + selectionMap = isRemote ? selectionWithIndexes : + selectionWithIndexes.filter((tuple) => tuple[0] < source.length); } if (this.cellSelection === GridSelectionMode.single && activeEl) { 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 0610c491e63..6e411772291 100644 --- a/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts +++ b/projects/igniteui-angular/src/lib/grids/selection/selection.service.ts @@ -25,8 +25,8 @@ export class IgxGridSelectionService { public pointerState = {} as ISelectionPointerState; public columnsState = {} as IColumnSelectionState; - public selection = new Map>(); - public temp = new Map>(); + public selection = new Map>(); + public temp = new Map>(); public rowSelection: Set = new Set(); public indeterminateRows: Set = new Set(); public columnSelection: Set = new Set(); @@ -38,7 +38,7 @@ export class IgxGridSelectionService { /** * @hidden @internal */ - public selectedRangeChange = new Subject>>(); + public selectedRangeChange = new Subject>>(); /** * Toggled when a pointerdown event is triggered inside the grid body (cells). @@ -118,10 +118,11 @@ export class IgxGridSelectionService { * Single clicks | Ctrl + single clicks on cells is the usual case. */ public add(node: ISelectionNode, addToRange = true): void { - if (this.selection.has(node.row)) { - this.selection.get(node.row).add(node.column); + const rowObject = this.getRowObjectByIndex(node.row); + if (this.selection.has(rowObject)) { + this.selection.get(rowObject).add(node.column); } else { - this.selection.set(node.row, new Set()).get(node.row).add(node.column); + this.selection.set(rowObject, new Set()).get(rowObject).add(node.column); } if (addToRange) { @@ -139,8 +140,9 @@ export class IgxGridSelectionService { } public remove(node: ISelectionNode): void { - if (this.selection.has(node.row)) { - this.selection.get(node.row).delete(node.column); + const rowObject = this.getRowObjectByIndex(node.row); + if (this.selection.has(rowObject)) { + this.selection.get(rowObject).delete(node.column); } if (this.isActiveNode(node)) { this.activeElement = null; @@ -149,8 +151,9 @@ export class IgxGridSelectionService { } public isInMap(node: ISelectionNode): boolean { - return (this.selection.has(node.row) && this.selection.get(node.row).has(node.column)) || - (this.temp.has(node.row) && this.temp.get(node.row).has(node.column)); + const rowObject = this.getRowObjectByIndex(node.row); + return (this.selection.has(rowObject) && this.selection.get(rowObject).has(node.column)) || + (this.temp.has(rowObject) && this.temp.get(rowObject).has(node.column)); } public selected(node: ISelectionNode): boolean { @@ -279,10 +282,10 @@ export class IgxGridSelectionService { this.selectRange(node, this.pointerState); } - public mergeMap(target: Map>, source: Map>): void { + public mergeMap(target: Map>, source: Map>): void { const iterator = source.entries(); let pair = iterator.next(); - let key: number; + let key: any; let value: Set; while (!pair.done) { @@ -346,17 +349,18 @@ export class IgxGridSelectionService { return false; } - public selectRange(node: ISelectionNode, state: SelectionState, collection: Map> = this.selection): void { + public selectRange(node: ISelectionNode, state: SelectionState, collection: Map> = this.selection): void { if (collection === this.temp) { collection.clear(); } const { rowStart, rowEnd, columnStart, columnEnd } = this.generateRange(node, state); for (let i = rowStart; i <= rowEnd; i++) { + const rowObject = this.getRowObjectByIndex(i); for (let j = columnStart as number; j <= (columnEnd as number); j++) { - if (collection.has(i)) { - collection.get(i).add(j); + if (collection.has(rowObject)) { + collection.get(rowObject).add(j); } else { - collection.set(i, new Set()).get(i).add(j); + collection.set(rowObject, new Set()).get(rowObject).add(j); } } } @@ -851,6 +855,31 @@ export class IgxGridSelectionService { selection.addRange(range); } + /** + * Gets the row object from the grid's dataView by index. + * Used as the key for the selection Map. + */ + private getRowObjectByIndex(rowIndex: number): any { + return this.grid.dataView[rowIndex]; + } + + /** + * Gets the row index from a row object by finding it in the dataView. + * Returns -1 if the row object is not found. + */ + public getRowIndexByObject(rowObject: any): number { + return this.grid.dataView.indexOf(rowObject); + } + + /** + * Returns the selected row indexes from the selection Map. + */ + public getSelectedCellRowIndexes(): number[] { + return Array.from(this.selection.keys()) + .map(rowObject => this.getRowIndexByObject(rowObject)) + .filter(index => index !== -1); + } + private isFilteringApplied(): boolean { return !FilteringExpressionsTree.empty(this.grid.filteringExpressionsTree) || !FilteringExpressionsTree.empty(this.grid.advancedFilteringExpressionsTree);