• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

IgniteUI / igniteui-angular / 29569403286

17 Jul 2026 09:16AM UTC coverage: 90.116% (-0.02%) from 90.135%
29569403286

Pull #15125

github

web-flow
Merge baede978b into 8ebabbb38
Pull Request #15125: refactor(*): bundle styles with components

14920 of 17397 branches covered (85.76%)

Branch coverage included in aggregate %.

30030 of 32483 relevant lines covered (92.45%)

34497.24 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.66
/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts
1
import {
2
    booleanAttribute,
3
    ContentChild,
4
    EventEmitter,
5
    Output,
6
    TemplateRef,
7
    inject,
8
    ContentChildren,
9
    QueryList,
10
    ChangeDetectionStrategy,
11
    ViewEncapsulation
12
} from '@angular/core';
13
import {
14
    Component, Input, ViewChild, OnDestroy, HostBinding
15
} from '@angular/core';
16
import { Subject } from 'rxjs';
17
import {
18
    EntityType,
19
    FieldType,
20
    IExpressionTree,
21
    IQueryBuilderResourceStrings,
22
    QueryBuilderResourceStringsEN,
23
    recreateTree,
24
    getCurrentResourceStrings,
25
    onResourceChangeHandle
26
} from 'igniteui-angular/core';
27
import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component';
28
import { IgxIconService } from 'igniteui-angular/icon';
29
import { editor } from '@igniteui/material-icons-extended';
30
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
31
import { IgxQueryBuilderSearchValueContext } from './query-builder.common';
32
import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component';
33

34
/* wcElementTag: igc-query-builder */
35
/* blazorIndirectRender */
36
/**
37
 * A component used for operating with complex filters by creating or editing conditions
38
 * and grouping them using AND/OR logic.
39
 * It is used internally in the Advanced Filtering of the Grid.
40
 *
41
 * @example
42
 * ```html
43
 * <igx-query-builder [entities]="this.entities">
44
 * </igx-query-builder>
45
 * ```
46
 */
47
@Component({
48
    selector: 'igx-query-builder',
49
    templateUrl: './query-builder.component.html',
50
    styleUrls: ['./query-builder.component.css'],
51
    encapsulation: ViewEncapsulation.None,
52
    changeDetection: ChangeDetectionStrategy.Eager,
53
    imports: [IgxQueryBuilderTreeComponent]
54
})
55
export class IgxQueryBuilderComponent implements OnDestroy {
3✔
56
    protected iconService = inject(IgxIconService);
161✔
57

58
    /**
59
     * @hidden @internal
60
     */
61
    @HostBinding('class.igx-query-builder')
62
    public cssClass = 'igx-query-builder';
161✔
63

64
    /**
65
     * @hidden @internal
66
     */
67
    @HostBinding('style.display')
68
    public display = 'block';
161✔
69

70
    /**
71
     * Gets/sets whether the confirmation dialog should be shown when changing entity.
72
     * Default value is `true`.
73
     */
74
    @Input({ transform: booleanAttribute })
75
    public showEntityChangeDialog = true;
161✔
76

77
    /**
78
     * Gets the list of entities available for the query builder.
79
     *
80
     * Each entity describes a logical group of fields that can be used in queries.
81
     * An entity can optionally have child entities, allowing nested sub-queries.
82
     *
83
     * @returns An array of {@link EntityType} objects.
84
     */
85
    public get entities(): EntityType[] {
86
        return this._entities;
57,804✔
87
    }
88

89
    /**
90
     * Sets the list of entities for the query builder.
91
     * If the `expressionTree` is defined, it will be recreated with the new entities.
92
     *
93
     * Each entity should be an {@link EntityType} object describing the fields and optionally child entities.
94
     *
95
     * Example:
96
     * ```ts
97
     * [
98
     *   {
99
     *     name: 'Orders',
100
     *     fields: [{ field: 'OrderID', dataType: 'number' }],
101
     *     childEntities: [
102
     *       {
103
     *         name: 'OrderDetails',
104
     *         fields: [{ field: 'ProductID', dataType: 'number' }]
105
     *       }
106
     *     ]
107
     *   }
108
     * ]
109
     * ```
110
     *
111
     * @param entities - The array of entities to set.
112
     */
113
    @Input()
114
    public set entities(entities: EntityType[]) {
115
        if (entities !== this._entities) {
161✔
116
            if (entities && this.expressionTree) {
161!
117
                this._expressionTree = recreateTree(this._expressionTree, entities);
×
118
            }
119
        }
120
        this._entities = entities;
161✔
121
    }
122

123
    /**
124
     * Gets the list of fields for the QueryBuilder.
125
     *
126
     * @deprecated since version 19.1.0. Use the `entities` property instead.
127
     * @hidden
128
     */
129
    public get fields(): FieldType[] {
130
        return this._fields;
×
131
    }
132

133
    /**
134
     * Sets the list of fields for the QueryBuilder.
135
     * Automatically wraps them into a single entity to maintain backward compatibility.
136
     *
137
     * @param fields - The array of fields to set.
138
     * @deprecated since version 19.1.0. Use the `entities` property instead.
139
     * @hidden
140
     */
141
    @Input()
142
    public set fields(fields: FieldType[]) {
143
        if (fields) {
×
144
            this._fields = fields;
×
145
            this.entities = [
×
146
                {
147
                    name: null,
148
                    fields: fields
149
                }
150
            ];
151
        }
152
    }
153

154
    /**
155
    * Returns the expression tree.
156
    */
157
    public get expressionTree(): IExpressionTree {
158
        return this._expressionTree;
5,479✔
159
    }
160

161
    /**
162
     * Sets the expression tree.
163
     */
164
    @Input()
165
    public set expressionTree(expressionTree: IExpressionTree) {
166
        if (expressionTree !== this._expressionTree) {
142✔
167
            if (this.entities && expressionTree) {
92✔
168
                this._expressionTree = recreateTree(expressionTree, this.entities);
86✔
169
            } else {
170
                this._expressionTree = expressionTree;
6✔
171
            }
172
        }
173
    }
174

175
    /**
176
     * Gets the `locale` of the query builder.
177
     * If not set, defaults to application's locale.
178
     */
179
    @Input()
180
    public locale: string;
181

182
    /**
183
     * Sets the resource strings.
184
     * By default it uses EN resources.
185
     */
186
    @Input()
187
    public set resourceStrings(value: IQueryBuilderResourceStrings) {
188
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
63✔
189
    }
190

191
    /**
192
     * Returns the resource strings.
193
     */
194
    public get resourceStrings(): IQueryBuilderResourceStrings {
195
        return this._resourceStrings || this._defaultResourceStrings;
5,273✔
196
    }
197

198
    /**
199
     * Disables subsequent entity changes at the root level after the initial selection.
200
     */
201
    @Input()
202
    public disableEntityChange = false;
161✔
203

204
    /**
205
     * Sets/gets the search value template.
206
     */
207
    @Input()
208
    public set searchValueTemplate(template: TemplateRef<IgxQueryBuilderSearchValueContext>) {
209
        this._searchValueTemplate = template;
×
210
    }
211

212
    public get searchValueTemplate(): TemplateRef<IgxQueryBuilderSearchValueContext> {
213
        return this._searchValueTemplate || this.searchValueTemplateDirective?.template;
5,271✔
214
    }
215

216
    /**
217
     * Disables return fields changes at the root level.
218
     */
219
    @Input()
220
    public disableReturnFieldsChange = false;
161✔
221

222
    /**
223
     * Event fired as the expression tree is changed.
224
     *
225
     * ```html
226
     *  <igx-query-builder (expressionTreeChange)='onExpressionTreeChange()'></igx-query-builder>
227
     * ```
228
     */
229
    @Output()
230
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
161✔
231

232
    /**
233
     * @hidden @internal
234
     */
235
    @ContentChild(IgxQueryBuilderSearchValueTemplateDirective)
236
    protected searchValueTemplateDirective: IgxQueryBuilderSearchValueTemplateDirective;
237

238

239

240
    /* contentChildren */
241
    /* blazorInclude */
242
    /* blazorTreatAsCollection */
243
    /* blazorCollectionName: QueryBuilderHeaderCollection */
244
    /* blazorCollectionItemName: QueryBuilderHeader */
245
    /* ngQueryListName: queryBuilderHeaderCollection */
246
    /** @hidden @internal */
247
    @ContentChildren(IgxQueryBuilderHeaderComponent)
248
    protected queryBuilderHeaderCollection: QueryList<IgxQueryBuilderHeaderComponent>;
249

250
    /**
251
     * @hidden @internal
252
     */
253
    @ViewChild(IgxQueryBuilderTreeComponent)
254
    public queryTree: IgxQueryBuilderTreeComponent;
255

256
    private destroy$ = new Subject<any>();
161✔
257
    private _resourceStrings: IQueryBuilderResourceStrings = null;
161✔
258
    private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN);
161✔
259
    private _expressionTree: IExpressionTree;
260
    private _fields: FieldType[];
261
    private _entities: EntityType[];
262
    private _shouldEmitTreeChange = true;
161✔
263
    private _searchValueTemplate: TemplateRef<IgxQueryBuilderSearchValueContext>;
264

265
    constructor() {
266
        this.registerSVGIcons();
161✔
267
        onResourceChangeHandle(this.destroy$, () => {
161✔
268
            this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false);
4✔
269
        }, this);
270
    }
271

272
    /**
273
     * Returns whether the expression tree can be committed in the current state.
274
     */
275
    public canCommit(): boolean {
276
        return this.queryTree?.canCommitCurrentState() === true;
28✔
277
    }
278

279
    /**
280
     * Commits the expression tree in the current state if it is valid. If not throws an exception.
281
     */
282
    public commit(): void {
283
        if (this.canCommit()) {
3✔
284
            this._shouldEmitTreeChange = false;
2✔
285
            this.queryTree.commitCurrentState();
2✔
286
            this._shouldEmitTreeChange = true;
2✔
287
        } else {
288
            throw new Error('Expression tree can\'t be committed in the current state. Use `canCommit` method to check if the current state is valid.');
1✔
289
        }
290
    }
291

292
    /**
293
     * Discards all unsaved changes to the expression tree.
294
     */
295
    public discard(): void {
296
        this.queryTree.cancelOperandEdit();
5✔
297
    }
298

299
    /**
300
     * @hidden @internal
301
     */
302
    public ngOnDestroy(): void {
303
        this.destroy$.next(true);
161✔
304
        this.destroy$.complete();
161✔
305
    }
306

307
    /**
308
     * @hidden @internal
309
     *
310
     * used by the grid
311
     */
312
    public get isContextMenuVisible(): boolean {
313
        return this.queryTree.isContextMenuVisible;
×
314
    }
315

316
    /**
317
     * @hidden @internal
318
     *
319
     * used by the grid
320
     */
321
    public exitOperandEdit() {
322
        this.queryTree.exitOperandEdit();
20✔
323
    }
324

325
    /**
326
     * @hidden @internal
327
     *
328
     * used by the grid
329
     */
330
    public setAddButtonFocus() {
331
        this.queryTree.setAddButtonFocus();
53✔
332
    }
333

334
    protected onExpressionTreeChange(tree: IExpressionTree) {
335
        if (tree && this.entities && tree !== this._expressionTree) {
128✔
336
            this._expressionTree = recreateTree(tree, this.entities);
110✔
337
        } else {
338
            this._expressionTree = tree;
18✔
339
        }
340
        if (this._shouldEmitTreeChange) {
128✔
341
            this.expressionTreeChange.emit(tree);
126✔
342
        }
343
    }
344

345
    private registerSVGIcons(): void {
346
        const editorIcons = editor as any[];
161✔
347

348
        editorIcons.forEach((icon) => {
161✔
349
            this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons');
9,660✔
350
            this.iconService.addIconRef(icon.name, 'default', {
9,660✔
351
                name: icon.name,
352
                family: 'imx-icons'
353
            });
354
        });
355

356
        const inIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M560-280H120v-400h720v120h-80v-40H200v240h360v80Zm-360-80v-240 240Zm560 200v-120H640v-80h120v-120h80v120h120v80H840v120h-80Z"/></svg>';
161✔
357
        this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons');
161✔
358
        this.iconService.addIconRef('in', 'default', {
161✔
359
            name: 'in',
360
            family: 'imx-icons'
361
        });
362

363
        const notInIcon = '<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#5f6368"><path d="M560-280H120v-400h720v120h-80v-40H200v240h360v80Zm-360-80v-240 240Zm440 104 84-84-84-84 56-56 84 84 84-84 56 56-83 84 83 84-56 56-84-83-84 83-56-56Z"/></svg>';
161✔
364
        this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons');
161✔
365
        this.iconService.addIconRef('not-in', 'default', {
161✔
366
            name: 'not-in',
367
            family: 'imx-icons'
368
        });
369

370
        this.iconService.addIconRef('add', 'default', {
161✔
371
            name: 'add',
372
            family: 'material',
373
        });
374

375
        this.iconService.addIconRef('close', 'default', {
161✔
376
            name: 'close',
377
            family: 'material',
378
        });
379

380
        this.iconService.addIconRef('check', 'default', {
161✔
381
            name: 'check',
382
            family: 'material',
383
        });
384

385
        this.iconService.addIconRef('delete', 'default', {
161✔
386
            name: 'delete',
387
            family: 'material',
388
        });
389

390
        this.iconService.addIconRef('edit', 'default', {
161✔
391
            name: 'edit',
392
            family: 'material',
393
        });
394
    }
395
}
396

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc