Skip to content
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes for each version of this project will be documented in this file.

## 20.2.0

### General
- `IgxHierarchicalGrid`
- **Deprecation** - `schema` input property has been deprecated and will be removed in a future version.

## 20.1.0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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, IgxHierarchicalGridMissingChildDataComponent } from '../hierarchical-grid/hierarchical-grid.spec';
import { IgxHGridRemoteOnDemandComponent, IgxHierarchicalGridMissingChildDataComponent, IgxHierarchicalGridToggleRIComponent } from '../hierarchical-grid/hierarchical-grid.spec';
import { IGridResourceStrings } from '../../core/i18n/grid-resources';

describe('IgxGrid - Advanced Filtering #grid - ', () => {
Expand Down Expand Up @@ -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();
// 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();
// 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');
// 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);

const fieldInputGroup = QueryBuilderFunctions.getQueryBuilderFieldsCombo(fixture, 1).querySelector('input');
expect(fieldInputGroup.value).toBe('ID');
// Verify return fields
QueryBuilderFunctions.clickQueryBuilderFieldsCombo(fixture, 1);
fixture.detectChanges();
const innerQueryReturnFields = QueryBuilderFunctions.getQueryBuilderSelectDropdown(queryBuilderElement, 1);

return innerQueryReturnFields;
};

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(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -590,11 +590,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
* const schema = this.grid.schema;
* this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }];
* ```
* @deprecated in version 20.2.0.
*/
@Input()
public set schema(entities: EntityType[]) {
this._hGridSchema = entities;
}
public set schema(entities: EntityType[]) {}

/* blazorSuppress */
public get schema() {
Expand Down Expand Up @@ -686,6 +685,10 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
this.batchEditing = val;
});
}
this.columnsAutogenerated.subscribe((e) => {
this.updateRootGridSchema(e);
});

super.ngOnInit();
}

Expand Down Expand Up @@ -1238,7 +1241,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) {
Expand All @@ -1248,6 +1251,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
fields: filterableFields.map(f => ({
field: f.field,
dataType: f.dataType,
label: f.label,
header: f.header,
editorOptions: f.editorOptions,
filters: f.filters,
Expand All @@ -1259,44 +1263,62 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti
];

entities[0].childEntities = this.childLayoutList.reduce((acc, rowIsland) => {
const childFirstRowData = this.data?.length > 0 && this.data[0][rowIsland.key]?.length > 0 ?
this.data[0][rowIsland.key][0] : null;
return acc.concat(this.generateChildEntity(rowIsland, childFirstRowData));
return acc.concat(this.generateChildEntity(rowIsland));
}
, []);
}

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 childEntity = schema[0];
for (let i = 0; i < path.length; i++) {
childEntity = childEntity.childEntities.find(e => e.name === path[i]);
}

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[];
}
}
}

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;
}
const childFirstRowData = firstRowData.length > 0 && firstRowData[childRowIsland.key]?.length > 0 ?
firstRowData[childRowIsland.key][0] : null;
return acc.concat(this.generateChildEntity(childRowIsland, childFirstRowData));
return acc.concat(this.generateChildEntity(childRowIsland));
}, []);

if (rowIslandChildEntities?.length > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

/**
Expand Down
Loading