From 7305e0ec42f5c70bde0f5a0c0a6ccd9e56e8d830 Mon Sep 17 00:00:00 2001 From: Martin Dragnev Date: Fri, 6 Mar 2026 16:33:33 +0200 Subject: [PATCH] fix(grid): Setup columns when autoGenerate is set after the grid data --- .../grids/core/src/watch-changes.ts | 3 ++- .../grids/grid/src/grid-base.directive.ts | 23 +++++++++++++++---- .../grids/grid/src/grid.component.spec.ts | 19 +++++++++++++++ .../src/row-island.component.ts | 2 +- .../pivot-grid/src/pivot-grid.component.ts | 2 +- 5 files changed, 41 insertions(+), 8 deletions(-) diff --git a/projects/igniteui-angular/grids/core/src/watch-changes.ts b/projects/igniteui-angular/grids/core/src/watch-changes.ts index d4db75c573a..cbd4bb75f28 100644 --- a/projects/igniteui-angular/grids/core/src/watch-changes.ts +++ b/projects/igniteui-angular/grids/core/src/watch-changes.ts @@ -22,7 +22,8 @@ export function WatchChanges(): PropertyDecorator { const oldValue = this[key]; if (val !== oldValue || (typeof val === 'object' && val === oldValue)) { originalSetter.call(this, val); - if (this.ngOnChanges && !init) { + // Explicitly check whether the decorator is called during initialization + if (this.ngOnChanges && init !== undefined && !init) { // in case wacthed prop changes trigger ngOnChanges manually const changes: SimpleChanges = { [key]: new SimpleChange(oldValue, val, false) diff --git a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts index e0eaf8817cb..a583c0f9837 100644 --- a/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts +++ b/projects/igniteui-angular/grids/grid/src/grid-base.directive.ts @@ -30,7 +30,9 @@ import { ViewContainerRef, DOCUMENT, inject, - InjectionToken + InjectionToken, + SimpleChanges, + OnChanges } from '@angular/core'; import { areEqualArrays, @@ -144,7 +146,7 @@ const MIN_ROW_EDITING_COUNT_THRESHOLD = 2; wcSkipComponentSuffix */ @Directive() export abstract class IgxGridBaseDirective implements GridType, - OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit { + OnInit, DoCheck, OnDestroy, AfterContentInit, AfterViewInit, OnChanges { /* blazorSuppress */ public readonly validation = inject(IgxGridValidationService); @@ -202,6 +204,7 @@ export abstract class IgxGridBaseDirective implements GridType, * * ``` */ + @WatchChanges() @Input({ transform: booleanAttribute }) public autoGenerate = false; @@ -4071,8 +4074,8 @@ export abstract class IgxGridBaseDirective implements GridType, const activeRow = this.navigation.activeNode?.row; const selectedCellIndexes = this.selectionService.selection - ? Array.from(this.selectionService.selection.keys()) - : []; + ? Array.from(this.selectionService.selection.keys()) + : []; this._activeRowIndexes = [activeRow, ...selectedCellIndexes]; return this._activeRowIndexes; } @@ -4294,6 +4297,16 @@ export abstract class IgxGridBaseDirective implements GridType, } } + /** + * @hidden @internal + */ + public ngOnChanges(changes: SimpleChanges) { + if (!changes.autoGenerate?.firstChange && changes.autoGenerate?.currentValue && this.data?.length > 0 && this.columnList?.length === 0) { + // Make sure to setup columns only after the grid is initialized and autoGenerate is changed + this.setupColumns(); + } + } + /** * @hidden * @internal @@ -6806,7 +6819,7 @@ export abstract class IgxGridBaseDirective implements GridType, } else if (this.width !== null) { this._columnWidth = Math.max(parseFloat(possibleWidth), this.minColumnWidth) + 'px' } else { - this._columnWidth = this.minColumnWidth + 'px'; + this._columnWidth = this.minColumnWidth + 'px'; } } this._updateColumnDefaultWidths(); diff --git a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts index 3dbf7acad9b..7a3e787aa83 100644 --- a/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts +++ b/projects/igniteui-angular/grids/grid/src/grid.component.spec.ts @@ -95,6 +95,25 @@ describe('IgxGrid Component Tests #grid', () => { expect(fix.componentInstance.columnEventCount).toEqual(4); }); + it('should initialize a grid with data and columns if autoGenerate is set after the data', () => { + const fix = TestBed.createComponent(IgxGridTestComponent); + fix.componentInstance.data = [ + { Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) } + ]; + fix.componentInstance.columns = []; + fix.detectChanges(); + + const grid = fix.componentInstance.grid; + + expect(grid.columns.length).toBe(0); + + fix.componentInstance.autoGenerate = true; + fix.detectChanges(); + + expect(grid.columns.length).toBe(4); + expect(grid.rowList.length).toBe(1); + }); + it('should initialize a grid and change column properties during initialization', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.componentInstance.columns = []; diff --git a/projects/igniteui-angular/grids/hierarchical-grid/src/row-island.component.ts b/projects/igniteui-angular/grids/hierarchical-grid/src/row-island.component.ts index 9f605bf95f1..98c4c02fca5 100644 --- a/projects/igniteui-angular/grids/hierarchical-grid/src/row-island.component.ts +++ b/projects/igniteui-angular/grids/hierarchical-grid/src/row-island.component.ts @@ -472,7 +472,7 @@ export class IgxRowIslandComponent extends IgxHierarchicalGridBaseDirective /** * @hidden */ - public ngOnChanges(changes) { + public override ngOnChanges(changes) { this.layoutChange.emit(changes); if (!this.isInit) { this.initialChanges.push(changes); diff --git a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts index afc3939de51..32294173321 100644 --- a/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts +++ b/projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts @@ -1020,7 +1020,7 @@ export class IgxPivotGridComponent extends IgxGridBaseDirective implements OnIni /** * @hidden @internal */ - public ngOnChanges(changes: SimpleChanges) { + public override ngOnChanges(changes: SimpleChanges) { if (changes.superCompactMode && !changes.superCompactMode.isFirstChange()) { this._shouldUpdateSizes = true; resizeObservable(this.verticalScrollContainer.displayContainer).pipe(take(1), takeUntil(this.destroy$)).subscribe(() => this.resizeNotify.next());