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 ca69837bb04..0cdd651c53a 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
@@ -15,13 +15,15 @@ import {
IgxGridAdvancedFilteringComponent,
IgxGridExternalAdvancedFilteringComponent,
IgxGridAdvancedFilteringBindingComponent,
- IgxGridAdvancedFilteringOverlaySettingsComponent
+ IgxGridAdvancedFilteringOverlaySettingsComponent,
+ IgxGridAdvancedFilteringDynamicColumnsComponent
} from '../../test-utils/grid-samples.spec';
import { ControlsFunction } from '../../test-utils/controls-functions.spec';
import { FormattedValuesFilteringStrategy } from '../../data-operations/filtering-strategy';
import { IgxHierGridExternalAdvancedFilteringComponent } from '../../test-utils/hierarchical-grid-components.spec';
import { IgxHierarchicalGridComponent } from '../hierarchical-grid/public_api';
import { IFilteringEventArgs } from '../public_api';
+import { SampleTestData } from '../../test-utils/sample-test-data.spec';
const ADVANCED_FILTERING_OPERATOR_LINE_AND_CSS_CLASS = 'igx-filter-tree__line--and';
const ADVANCED_FILTERING_OPERATOR_LINE_OR_CSS_CLASS = 'igx-filter-tree__line--or';
@@ -38,7 +40,8 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
IgxGridAdvancedFilteringComponent,
IgxGridExternalAdvancedFilteringComponent,
IgxGridAdvancedFilteringBindingComponent,
- IgxHierGridExternalAdvancedFilteringComponent
+ IgxHierGridExternalAdvancedFilteringComponent,
+ IgxGridAdvancedFilteringDynamicColumnsComponent
]
});
}));
@@ -2048,6 +2051,78 @@ describe('IgxGrid - Advanced Filtering #grid - ', () => {
expect(rows.length).toEqual(1, 'Wrong filtered rows count');
}));
+ it('should handle advanced filtering correctly when grid columns and data are dynamically changed', fakeAsync(() => {
+ const fixture = TestBed.createComponent(IgxGridAdvancedFilteringDynamicColumnsComponent);
+ grid = fixture.componentInstance.grid;
+ fixture.detectChanges();
+
+ expect(grid.filteredData).toBeNull();
+ expect(grid.rowList.length).toBe(8);
+ expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
+ expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('NetAdvantage');
+
+ // Open Advanced Filtering dialog
+ GridFunctions.clickAdvancedFilteringButton(fixture);
+ fixture.detectChanges();
+
+ // Click the initial 'Add And Group' button
+ GridFunctions.clickAdvancedFilteringInitialAddGroupButton(fixture, 0);
+ tick(100);
+ fixture.detectChanges();
+
+ // Populate edit inputs
+ selectColumnInEditModeExpression(fixture, 1);
+ selectOperatorInEditModeExpression(fixture, 2);
+ const input = GridFunctions.getAdvancedFilteringValueInput(fixture).querySelector('input');
+ UIInteractions.clickAndSendInputElementValue(input, 'ign', fixture);
+
+ // Commit the populated expression
+ GridFunctions.clickAdvancedFilteringExpressionCommitButton(fixture);
+ fixture.detectChanges();
+
+ // Apply the filters
+ GridFunctions.clickAdvancedFilteringApplyButton(fixture);
+ fixture.detectChanges();
+
+ // Verify the filter results
+ expect(grid.filteredData.length).toEqual(2);
+ expect(grid.rowList.length).toBe(2);
+ expect(GridFunctions.getCurrentCellFromGrid(grid, 0, 1).value).toBe('Ignite UI for JavaScript');
+ expect(GridFunctions.getCurrentCellFromGrid(grid, 1, 1).value).toBe('Ignite UI for Angular');
+
+ // Change the grid's columns collection
+ fixture.componentInstance.columns = [
+ { field: 'ID', header: 'ID', width: '200px', type: 'string' },
+ { field: 'CompanyName', header: 'Company Name', width: '200px', type: 'string'},
+ { field: 'ContactName', header: 'Contact Name', width: '200px', type: 'string' },
+ { field: 'ContactTitle', header: 'Contact Title', width: '200px', type: 'string' },
+ { field: 'City', header: 'City', width: '200px', type: 'string' },
+ { field: 'Country', header: 'Country', width: '200px', type: 'string' },
+ ];
+ fixture.detectChanges();
+ flush();
+
+ // Change the grid's data collection
+ grid.data = SampleTestData.contactInfoDataFull();
+ fixture.detectChanges();
+ flush();
+
+ // Spy for error messages in the console
+ const consoleSpy = spyOn(console, 'error');
+
+ // Open Advanced Filtering dialog
+ GridFunctions.clickAdvancedFilteringButton(fixture);
+ fixture.detectChanges();
+ flush();
+
+ // Verify the filters are cleared
+ expect(grid.filteredData).toEqual([]);
+ expect(grid.rowList.length).toBe(0);
+
+ // Check for error messages in the console
+ expect(consoleSpy).not.toHaveBeenCalled();
+ }));
+
describe('Context Menu - ', () => {
it('Should discard added group when clicking its operator line without having a single expression.', fakeAsync(() => {
// Open Advanced Filtering dialog.
diff --git a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts
index efbff961c69..0453a728d01 100644
--- a/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts
+++ b/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts
@@ -988,31 +988,33 @@ export class IgxQueryBuilderComponent implements AfterViewInit, OnDestroy {
this.currentGroup = groupItem;
}
- private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem {
- let groupItem: ExpressionGroupItem;
- if (expressionTree) {
- groupItem = new ExpressionGroupItem(expressionTree.operator, parent);
-
- for (const expr of expressionTree.filteringOperands) {
- if (expr instanceof FilteringExpressionsTree) {
- groupItem.children.push(this.createExpressionGroupItem(expr, groupItem));
- } else {
- const filteringExpr = expr as IFilteringExpression;
- const exprCopy: IFilteringExpression = {
- fieldName: filteringExpr.fieldName,
- condition: filteringExpr.condition,
- searchVal: filteringExpr.searchVal,
- ignoreCase: filteringExpr.ignoreCase
- };
+ private createExpressionGroupItem(expressionTree: IExpressionTree, parent?: ExpressionGroupItem): ExpressionGroupItem | null {
+ if (!expressionTree) {
+ return null;
+ }
+
+ const groupItem = new ExpressionGroupItem(expressionTree.operator, parent);
+
+ for (const expr of expressionTree.filteringOperands) {
+ if (expr instanceof FilteringExpressionsTree) {
+ const childGroup = this.createExpressionGroupItem(expr, groupItem);
+ if (childGroup) {
+ groupItem.children.push(childGroup);
+ }
+ } else {
+ const filteringExpr = expr as IFilteringExpression;
+ const field = this.fields.find(el => el.field === filteringExpr.fieldName);
+
+ if (field) {
+ const exprCopy: IFilteringExpression = { ...filteringExpr };
const operandItem = new ExpressionOperandItem(exprCopy, groupItem);
- const field = this.fields.find(el => el.field === filteringExpr.fieldName);
operandItem.fieldLabel = field.label || field.header || field.field;
groupItem.children.push(operandItem);
}
}
}
- return groupItem;
+ return groupItem.children.length > 0 ? groupItem : null;
}
private createExpressionTreeFromGroupItem(groupItem: ExpressionGroupItem): FilteringExpressionsTree {
diff --git a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts
index 32d20e821eb..f435bd2df80 100644
--- a/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts
+++ b/projects/igniteui-angular/src/lib/test-utils/grid-samples.spec.ts
@@ -1174,6 +1174,37 @@ export class IgxGridAdvancedFilteringComponent extends BasicGridComponent {
}
}
+@Component({
+ template: `
+
+
+
+ `,
+ standalone: true,
+ imports: [NgFor, IgxGridComponent, IgxColumnComponent, IgxGridToolbarComponent]
+})
+export class IgxGridAdvancedFilteringDynamicColumnsComponent extends BasicGridComponent {
+ public override data = [];
+ public columns = [];
+
+ public ngOnInit(): void {
+ this.columns = [
+ { field: 'ID', header: 'HeaderID', width: '100px', type: 'number' },
+ { field: 'ProductName', header: 'Product Name', width: '200px', type: 'string'},
+ { field: 'Downloads', header: 'Downloads', width: '100px', type: 'number' },
+ { field: 'Released', header: 'Released', width: '100px', type: 'boolean' },
+ { field: 'ReleaseDate', header: 'Release Date', width: '200px', type: 'date' },
+ { field: 'AnotherField', header: 'Another Field', width: '200px', type: 'string' },
+ ];
+ this.data = SampleTestData.excelFilteringData();
+ }
+}
+
@Component({
template: `