Repository navigation
feat(h-grid): update adv filtering schema on row expand #15927
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
28eec6b
3cc7307
28f8a35
d923eaa
e89ab5e
2568b77
a418259
5974cb3
fa96ebd
4501c70
0be78a3
913063e
2ce115e
36d111f
120a16a
7ef19e9
c804e00
26a8fb5
eb2dc93
1b45c06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -460,6 +460,7 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| } | ||
|
|
||
| private _data: any; | ||
| private _schemaSetByUser = false; | ||
| private h_id = `igx-hierarchical-grid-${NEXT_ID++}`; | ||
| private childGridTemplates: Map<any, any> = new Map(); | ||
|
|
||
|
|
@@ -581,16 +582,21 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| * @Input() | ||
| * @param {EntityType[]} entities - An array of EntityType objects representing the grid's schema. | ||
| * @remarks | ||
| * This property is required in remote data filtering scenarios. | ||
| * When not set, the schema is generated from the filterable columns of the grid and from the row islands. | ||
| * The fields of auto-generated row islands are resolved from the child data and are updated when | ||
| * the child grids generate their columns, e.g. when the child data is loaded on demand. | ||
| * @example | ||
| * ```typescript | ||
| * const schema = this.grid.schema; | ||
| * this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }]; | ||
| * ``` | ||
| * @deprecated in version 22.3.0. Define the columns and the row islands of the grid instead, | ||
| * the schema is generated from them. | ||
| */ | ||
| @Input() | ||
| public set schema(entities: EntityType[]) { | ||
| this._hGridSchema = entities; | ||
| this._schemaSetByUser = !!entities; | ||
| } | ||
|
|
||
| /* blazorSuppress */ | ||
|
|
@@ -1258,6 +1264,9 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| this.createColumnsList(this.parentIsland.childColumns.toArray()); | ||
| } else { | ||
| super.setupColumns(); | ||
| if (this.autoGenerate && this.columns.length > 0) { | ||
| this.updateGeneratedSchemaFields(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -1276,6 +1285,9 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| protected override onColumnsChanged() { | ||
| Promise.resolve().then(() => { | ||
| this.updateColumnList(); | ||
| if (!this.parent) { | ||
| this.updateGeneratedSchemaFields(); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
|
|
@@ -1311,23 +1323,14 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| } | ||
|
|
||
| private generateSchema() { | ||
| const filterableFields = this.columns.filter((column) => !column.columnGroup && column.filterable); | ||
| const filterableFields = this.getFilterableFields(this.columns); | ||
| let entities!: EntityType[]; | ||
|
|
||
| if(filterableFields.length !== 0) { | ||
| entities = [ | ||
| { | ||
| name: null!, | ||
| fields: filterableFields.map(f => ({ | ||
| field: f.field, | ||
| dataType: f.dataType, | ||
| header: f.header, | ||
| editorOptions: f.editorOptions, | ||
| filters: f.filters, | ||
| pipeArgs: f.pipeArgs, | ||
| defaultTimeFormat: f.defaultTimeFormat, | ||
| defaultDateTimeFormat: f.defaultDateTimeFormat | ||
| })) as FieldType[] | ||
| fields: filterableFields | ||
| } | ||
| ]; | ||
|
|
||
|
|
@@ -1342,37 +1345,144 @@ export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirecti | |
| return entities; | ||
| } | ||
|
|
||
| private getFilterableFields(columns: IgxColumnComponent[]): FieldType[] { | ||
| return this.toFieldTypes(columns.filter((column) => !column.columnGroup && column.filterable)); | ||
| } | ||
|
|
||
| private toFieldTypes(columns: IgxColumnComponent[]): FieldType[] { | ||
| return columns.map(f => ({ | ||
| field: f.field, | ||
| dataType: f.dataType, | ||
| header: f.header, | ||
| editorOptions: f.editorOptions, | ||
| filters: f.filters, | ||
| pipeArgs: f.pipeArgs, | ||
| defaultTimeFormat: f.defaultTimeFormat, | ||
| defaultDateTimeFormat: f.defaultDateTimeFormat | ||
| })) as FieldType[]; | ||
| } | ||
|
|
||
| /** | ||
| * Updates the fields of the generated schema entity that corresponds to this grid with its current columns, | ||
| * so that columns auto-generated after the schema was created (e.g. for child data loaded on demand) | ||
| * become available in the advanced filtering dialog. A schema set by the user is not modified. | ||
| */ | ||
| private updateGeneratedSchemaFields() { | ||
| const root = this.rootGrid as IgxHierarchicalGridComponent; | ||
| const fields = this.parentIsland ? this.getChildGridFields(this.columns) : this.getFilterableFields(this.columns); | ||
| const entity = root.setGeneratedSchemaFields(this.parentIsland, fields); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Editing a filter in the Advanced Filtering dialog throws after expanding a row whose first child record has |
||
| const firstRecord = this.data?.[0]; | ||
| if (entity && this.parentIsland && firstRecord) { | ||
| // nested row islands that have no fields yet (e.g. their parent data is loaded on demand) | ||
| // are resolved from the first record of this grid, as they are when the schema is generated | ||
| root.fillEmptySchemaFields(entity, root.generateChildEntity(this.parentIsland, firstRecord)); | ||
| } | ||
| } | ||
|
|
||
| /** Sets the fields of the entities that have none from the matching entities of another entity tree. */ | ||
| private fillEmptySchemaFields(target: EntityType, source: EntityType) { | ||
| if (!target.fields?.length) { | ||
| target.fields = source.fields; | ||
| } | ||
| source.childEntities?.forEach(sourceChild => { | ||
| const targetChild = target.childEntities?.find(e => e.name === sourceChild.name); | ||
| if (targetChild) { | ||
| this.fillEmptySchemaFields(targetChild, sourceChild); | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Resolves the fields of an auto-generated row island from the columns of one of its child grids. | ||
| * Columns that are not filterable are kept, so that expression trees that already use them can still be recreated. | ||
| */ | ||
| private getChildGridFields(columns: IgxColumnComponent[]): FieldType[] { | ||
| return this.toFieldTypes(columns.filter((column) => !column.columnGroup && column.field)); | ||
| } | ||
|
|
||
| /** | ||
| * Updates the fields of the generated schema entity that corresponds to a row island with declared columns, | ||
| * so that columns added, removed or changed after the schema was created are reflected | ||
| * in the advanced filtering dialog. A schema set by the user is not modified. | ||
| * | ||
| * @hidden @internal | ||
| */ | ||
| public updateRowIslandSchemaFields(rowIsland: IgxRowIslandComponent) { | ||
| if (!rowIsland.autoGenerate) { | ||
| this.setGeneratedSchemaFields(rowIsland, this.getRowIslandFields(rowIsland)); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Sets the fields of the generated schema entity of a row island, or of the root entity when no row island is passed. | ||
| * Returns the updated entity, if any. | ||
| */ | ||
| private setGeneratedSchemaFields(rowIsland: IgxRowIslandComponent | null | undefined, fields: FieldType[]): EntityType | undefined { | ||
| if (this._schemaSetByUser || !this._hGridSchema?.length) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const path: string[] = []; | ||
| let island = rowIsland; | ||
| while (island) { | ||
| path.unshift(island.key); | ||
| island = island.parentIsland; | ||
| } | ||
|
|
||
| let entity: EntityType | undefined = this._hGridSchema[0]; | ||
| for (const key of path) { | ||
| entity = entity?.childEntities?.find(e => e.name === key); | ||
| } | ||
|
|
||
| if (entity) { | ||
| entity.fields = fields; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applying a saved advanced filter throws after expanding a row whose first child record has |
||
| } | ||
| return entity; | ||
| } | ||
|
|
||
| /** | ||
| * Resolves the fields of a row island with declared columns, including the ones in column groups. | ||
| * Columns that are not filterable are kept, so that expression trees that already use them can still be recreated. | ||
| */ | ||
| private getRowIslandFields(rowIsland: IgxRowIslandComponent): FieldType[] { | ||
| return this.toFieldTypes(flatten(rowIsland.childColumns.toArray()).filter(col => col.field)); | ||
| } | ||
|
|
||
| private generateChildEntity(rowIsland: IgxRowIslandComponent, firstRowData: any): EntityType { | ||
| const entityName = rowIsland.key; | ||
| let fields: any[] = []; | ||
| let childEntities; | ||
| // a child grid that is already created has its columns auto-generated from the actual child data, | ||
| // with the header, data type and formatting options set in columnInit | ||
| const childGrid = rowIsland.autoGenerate && | ||
| rowIsland.rowIslandAPI.getChildGrids().find(grid => grid.columns.length > 0); | ||
| const childGridFields = childGrid ? this.getChildGridFields(childGrid.columns) : []; | ||
| if (!rowIsland.autoGenerate) { | ||
| fields = flatten(rowIsland.childColumns.toArray()).filter(col => col.field) | ||
| .map(f => ({ field: f.field, dataType: f.dataType })) as FieldType[]; | ||
| fields = this.getRowIslandFields(rowIsland); | ||
| } else if (firstRowData) { | ||
| const rowIslandFields = Object.keys(firstRowData).map(key => { | ||
| if (firstRowData[key] instanceof Array) { | ||
| return null; | ||
| } | ||
|
|
||
| return { | ||
| return childGridFields.find(f => f.field === key) ?? { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With no filter applied, the Advanced Filtering dialog shows text conditions for a number field after expanding a row whose first child record has |
||
| field: key, | ||
| dataType: this.resolveDataTypes(firstRowData[key]) | ||
| } | ||
| }; | ||
| }); | ||
| fields = rowIslandFields.filter(f => f !== null) as FieldType[]; | ||
| } else { | ||
| fields = childGridFields; | ||
| } | ||
|
|
||
| // the child entities are generated from the row islands even if there is no child data yet (e.g. load on demand) | ||
| const rowIslandChildEntities = rowIsland.childLayoutList.reduce((acc, childRowIsland) => { | ||
| if (!firstRowData) { | ||
| return null; | ||
| } | ||
| const childFirstRowData = firstRowData[childRowIsland.key]?.length > 0 ? | ||
| const childFirstRowData = firstRowData?.[childRowIsland.key]?.length > 0 ? | ||
| firstRowData[childRowIsland.key][0] : null; | ||
| return acc.concat(this.generateChildEntity(childRowIsland, childFirstRowData)); | ||
| }, [] as any); | ||
| }, [] as EntityType[]); | ||
|
|
||
| if (rowIslandChildEntities?.length > 0) { | ||
| if (rowIslandChildEntities.length > 0) { | ||
| childEntities = rowIslandChildEntities; | ||
| } | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.