From 28eec6be09e8af659f25c6cf4f37f604925d8710 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 11 Jun 2025 20:08:31 +0300 Subject: [PATCH 1/5] feat(h-grid): update adv filtering schema on row expand --- .../grid/grid-filtering-advanced.spec.ts | 119 +++++++++--------- .../hierarchical-grid.component.ts | 69 ++++++---- 2 files changed, 103 insertions(+), 85 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts index fce5caf5528..258a01f67f3 100644 --- a/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts +++ b/projects/igniteui-angular/src/lib/grids/grid/grid-filtering-advanced.spec.ts @@ -27,7 +27,7 @@ import { QueryBuilderFunctions } from '../../query-builder/query-builder-functio import { By } from '@angular/platform-browser'; import { IgxDateTimeEditorDirective } from '../../directives/date-time-editor/date-time-editor.directive'; import { QueryBuilderSelectors } from '../../query-builder/query-builder.common'; -import { IgxHGridRemoteOnDemandComponent } from '../hierarchical-grid/hierarchical-grid.spec'; +import { IgxHGridRemoteOnDemandComponent, IgxHierarchicalGridToggleRIComponent } from '../hierarchical-grid/hierarchical-grid.spec'; import { IGridResourceStrings } from '../../core/i18n/grid-resources'; describe('IgxGrid - Advanced Filtering #grid - ', () => { @@ -1750,76 +1750,69 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => { expect(hgrid.filteredData.length).toBe(5); })); - it('Should have proper fields in UI when schema is defined with load on demand.', fakeAsync(() => { - const fixture = TestBed.createComponent(IgxHGridRemoteOnDemandComponent); - const hierarchicalGrid = fixture.componentInstance.instance; + it('Should update root grid schema when row island is expanded.', fakeAsync(() => { + const fixture = TestBed.createComponent(IgxHierarchicalGridToggleRIComponent); + const hierarchicalGrid = fixture.componentInstance.hgrid; hierarchicalGrid.allowAdvancedFiltering = true; - hierarchicalGrid.schema = [ - { - name: 'rootLevel', - fields: [ - { field: 'ID', dataType: 'string' }, - { field: 'ChildLevels', dataType: 'number' }, - { field: 'ProductName', dataType: 'string' }, - { field: 'Col1', dataType: 'number' }, - { field: 'Col2', dataType: 'number' }, - { field: 'Col3', dataType: 'number' } - ], - childEntities: [ - { - name: 'childData', - fields: [ - { field: 'ID', dataType: 'string' }, - { field: 'ProductName', dataType: 'string' } - ], - childEntities: [ - { - name: 'childData2', - fields: [ - { field: 'ID', dataType: 'string' }, - { field: 'ProductName', dataType: 'string' } - ] - } - ] - } - ] - } - ] - fixture.detectChanges(); - - hierarchicalGrid.openAdvancedFilteringDialog(); fixture.detectChanges(); - // Click the initial 'Add Condition' button. - QueryBuilderFunctions.clickQueryBuilderInitialAddConditionBtn(fixture, 0); - tick(100); - fixture.detectChanges(); - // Populate edit inputs. - QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 0); // Select 'ID' column. - QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 10); // Select 'In' operator. - tick(100); - fixture.detectChanges(); - - const entityInputGroup = QueryBuilderFunctions.getQueryBuilderEntitySelect(fixture, 1).querySelector('input'); - expect(entityInputGroup.value).toBe('childData'); + // Open advanced filtering dialog and create an 'In' condition. + const createInConditionAndGetReturnFields = () => { + // Open Advanced Filtering dialog. + hierarchicalGrid.openAdvancedFilteringDialog(); + fixture.detectChanges(); + + // Click the initial 'Add Condition' button. + QueryBuilderFunctions.clickQueryBuilderInitialAddConditionBtn(fixture, 0); + tick(100); + fixture.detectChanges(); + // Populate edit inputs. + QueryBuilderFunctions.selectColumnInEditModeExpression(fixture, 0); // Select 'ID' column. + QueryBuilderFunctions.selectOperatorInEditModeExpression(fixture, 10); // Select 'In' operator. + tick(100); + fixture.detectChanges(); + + // Verify entities + QueryBuilderFunctions.clickQueryBuilderEntitySelect(fixture, 1); + fixture.detectChanges(); + const queryBuilderElement: HTMLElement = fixture.debugElement.queryAll(By.css(`.${QueryBuilderSelectors.QUERY_BUILDER_TREE}`))[1].nativeElement; + let dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); + let expectedValues = ['childData']; + expect(dropdownValues).toEqual(expectedValues); + + // Verify return fields + QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fixture, 1); + fixture.detectChanges(); + const innerQueryReturnFields = QueryBuilderFunctions.getQueryBuilderSelectDropdown(queryBuilderElement, 1); + + return innerQueryReturnFields; + }; - const fieldInputGroup = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fixture, 1).querySelector('input'); - expect(fieldInputGroup.value).toBe('ID'); + let innerQueryReturnFields = createInConditionAndGetReturnFields(); + expect(innerQueryReturnFields).toBeNull(); - // Verify entities - QueryBuilderFunctions.clickQueryBuilderEntitySelect(fixture, 1); + // Close Advanced Filtering dialog. + hierarchicalGrid.closeAdvancedFilteringDialog(false); + tick(200); fixture.detectChanges(); - const queryBuilderElement: HTMLElement = fixture.debugElement.queryAll(By.css(`.${QueryBuilderSelectors.QUERY_BUILDER_TREE}`))[1].nativeElement; - let dropdownValues: string[] = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement).map((x: any) => x.innerText); - let expectedValues = ['childData']; - expect(dropdownValues).toEqual(expectedValues); - // Verify return fileds - QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fixture, 1); + // Expand row island + const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as any; + UIInteractions.simulateClickAndSelectEvent(row.expander); fixture.detectChanges(); - dropdownValues = QueryBuilderFunctions.getQueryBuilderSelectDropdownItems(queryBuilderElement, 1).map((x: any) => x.innerText); - expectedValues = ['ID', 'ProductName']; - expect(dropdownValues).toEqual(expectedValues); + + // Open advanced filtering dialog and verify that schema is updated. + innerQueryReturnFields = createInConditionAndGetReturnFields(); + expect(innerQueryReturnFields).not.toBeNull(); + const items = Array.from(innerQueryReturnFields.querySelectorAll('.igx-drop-down__item')); + expect(items.length).toBe(7); + expect((items[0] as HTMLElement).innerText).toBe('ID'); + expect((items[1] as HTMLElement).innerText).toBe('ChildLevels'); + expect((items[2] as HTMLElement).innerText).toBe('ProductName'); + expect((items[3] as HTMLElement).innerText).toBe('Col1'); + expect((items[4] as HTMLElement).innerText).toBe('Col2'); + expect((items[5] as HTMLElement).innerText).toBe('Col3'); + expect((items[6] as HTMLElement).innerText).toBe('childData2'); })); it('Should correctly change resource strings for hierarchical Advanced Filtering dialog.', fakeAsync(() => { diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 69695e21ee2..2ad9af0cb09 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -587,11 +587,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti * const schema = this.grid.schema; * this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }]; * ``` + * @deprecated in version 20.1.0. */ @Input() - public set schema(entities: EntityType[]) { - this._hGridSchema = entities; - } + public set schema(entities: EntityType[]) {} /* blazorSuppress */ public get schema() { @@ -683,6 +682,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti this.batchEditing = val; }); } + this.columnsAutogenerated.subscribe((e) => { + this.updateRootGridSchema(e); + }); + super.ngOnInit(); } @@ -1256,7 +1259,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti ]; entities[0].childEntities = this.childLayoutList.reduce((acc, rowIsland) => { - return acc.concat(this.generateChildEntity(rowIsland, this.data[0][rowIsland.key][0])); + return acc.concat(this.generateChildEntity(rowIsland)); } , []); } @@ -1264,32 +1267,54 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti return entities; } - private generateChildEntity(rowIsland: IgxRowIslandComponent, firstRowData: any[]): EntityType { + private updateRootGridSchema(event: any) { + const schema = (this.rootGrid as IgxHierarchicalGridComponent).schema; + if (!schema || schema.length === 0) { + return; + } + + let path = []; + let parentIsland = this.parentIsland; + while (parentIsland) { + path.push(parentIsland.key); + parentIsland = parentIsland.parentIsland; + } + + path.reverse(); + if (path.length > 0) { + let childLevel = path[0]; + let childEntity; + while (childLevel) { + childEntity = schema[0].childEntities.find(e => e.name === childLevel); + childLevel = path.shift(); + } + + childEntity.fields = event.columns.filter((column) => !column.columnGroup && column.filterable) + .map(f => ({ + field: f.field, + dataType: f.dataType, + label: f.label, + header: f.header, + editorOptions: f.editorOptions, + filters: f.filters, + pipeArgs: f.pipeArgs, + defaultTimeFormat: f.defaultTimeFormat, + defaultDateTimeFormat: f.defaultDateTimeFormat + })) as FieldType[]; + } + } + + private generateChildEntity(rowIsland: IgxRowIslandComponent): EntityType { const entityName = rowIsland.key; let fields = []; let childEntities; - if (!rowIsland.autoGenerate) { + if (rowIsland.childColumns?.length > 0) { fields = flatten(rowIsland.childColumns.toArray()).filter(col => col.field) .map(f => ({ field: f.field, dataType: f.dataType })) as FieldType[]; - } else if (firstRowData) { - const rowIslandFields = Object.keys(firstRowData).map(key => { - if (firstRowData[key] instanceof Array) { - return null; - } - - return { - field: key, - dataType: this.resolveDataTypes(firstRowData[key]) - } - }); - fields = rowIslandFields.filter(f => f !== null) as FieldType[]; } const rowIslandChildEntities = rowIsland.childLayoutList.reduce((acc, childRowIsland) => { - if (!firstRowData) { - return null; - } - return acc.concat(this.generateChildEntity(childRowIsland, firstRowData[childRowIsland.key][0])); + return acc.concat(this.generateChildEntity(childRowIsland)); }, []); if (rowIslandChildEntities?.length > 0) { From 28f8a351792d2a4b0bb31c797f4cca6c0fe6b85d Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Fri, 4 Jul 2025 16:29:41 +0300 Subject: [PATCH 2/5] fix(adv-filtering): improve child entities fields population on row expand --- .../hierarchical-grid.component.ts | 40 +++++++++---------- .../query-builder-tree.component.ts | 2 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 2ad9af0cb09..b44aff06c52 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -1237,7 +1237,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti } private generateSchema() { - const filterableFields = this.columns.filter((column) => !column.columnGroup && column.filterable); + const filterableFields: FieldType[] = this.columns.filter((column) => !column.columnGroup && column.filterable); let entities: EntityType[]; if(filterableFields.length !== 0) { @@ -1247,8 +1247,8 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti fields: filterableFields.map(f => ({ field: f.field, dataType: f.dataType, - // label: f.label, - // header: f.header, + label: f.label, + header: f.header, editorOptions: f.editorOptions, filters: f.filters, pipeArgs: f.pipeArgs, @@ -1282,25 +1282,25 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti path.reverse(); if (path.length > 0) { - let childLevel = path[0]; - let childEntity; - while (childLevel) { - childEntity = schema[0].childEntities.find(e => e.name === childLevel); - childLevel = path.shift(); + let childEntity = schema[0]; + for (let i = 0; i < path.length; i++) { + childEntity = childEntity.childEntities.find(e => e.name === path[i]); } - childEntity.fields = event.columns.filter((column) => !column.columnGroup && column.filterable) - .map(f => ({ - field: f.field, - dataType: f.dataType, - label: f.label, - header: f.header, - editorOptions: f.editorOptions, - filters: f.filters, - pipeArgs: f.pipeArgs, - defaultTimeFormat: f.defaultTimeFormat, - defaultDateTimeFormat: f.defaultDateTimeFormat - })) as FieldType[]; + if (childEntity) { + childEntity.fields = event.columns.filter((column) => !column.columnGroup && column.filterable) + .map(f => ({ + field: f.field, + dataType: f.dataType, + label: f.label, + header: f.header, + editorOptions: f.editorOptions, + filters: f.filters, + pipeArgs: f.pipeArgs, + defaultTimeFormat: f.defaultTimeFormat, + defaultDateTimeFormat: f.defaultDateTimeFormat + })) as FieldType[]; + } } } diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts index 7a59165e80c..36f23c9ce98 100644 --- a/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts +++ b/projects/igniteui-angular/src/lib/query-builder/query-builder-tree.component.ts @@ -1410,7 +1410,7 @@ export class IgxQueryBuilderTreeComponent implements AfterViewInit, OnDestroy { * @hidden @internal */ public getFormat(field: string) { - return this.fields?.find(el => el.field === field).pipeArgs.format; + return this.fields?.find(el => el.field === field)?.pipeArgs.format; } /** From d923eaaa24f80e4abad08d7caca15d7ca1d88924 Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Wed, 16 Jul 2025 14:32:38 +0300 Subject: [PATCH 3/5] chore(*): add deprecation of schema property to changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fe5277e2a5..500f2267921 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes for each version of this project will be documented in this file. +## 20.1.0 + +### General +- `IgxHierarchicalGrid` + - **Deprecation** - `schema` input property has been deprecated and will be removed in a future version. + ## 20.0.2 ### New Features From a418259c525a55843aaaf0864a8aef992c2748ee Mon Sep 17 00:00:00 2001 From: igdmdimitrov Date: Tue, 7 Oct 2025 13:42:27 +0300 Subject: [PATCH 4/5] chore(*): update deprecation comment --- .../lib/grids/hierarchical-grid/hierarchical-grid.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 154fa3d581a..8a10cd7dfdd 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -590,7 +590,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti * const schema = this.grid.schema; * this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }]; * ``` - * @deprecated in version 20.1.0. + * @deprecated in version 21.0.0. */ @Input() public set schema(entities: EntityType[]) {} From fa96ebd7d90da6f5b438e91f299bd2ecf2737304 Mon Sep 17 00:00:00 2001 From: igdmdimitrov <49060557+igdmdimitrov@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:11:48 +0300 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Galina Edinakova --- CHANGELOG.md | 2 +- .../lib/grids/hierarchical-grid/hierarchical-grid.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5217b1e22d..b39b7e1ac66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ All notable changes for each version of this project will be documented in this file. -## 21.0.0 +## 20.2.0 ### General - `IgxHierarchicalGrid` diff --git a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts index 8a10cd7dfdd..e306d13a54b 100644 --- a/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts +++ b/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts @@ -590,7 +590,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti * const schema = this.grid.schema; * this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }]; * ``` - * @deprecated in version 21.0.0. + * @deprecated in version 20.2.0. */ @Input() public set schema(entities: EntityType[]) {}