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

IgniteUI / igniteui-angular / 28013325390

23 Jun 2026 08:34AM UTC coverage: 90.139% (-0.02%) from 90.154%
28013325390

Pull #17324

github

web-flow
Merge 690ff31c5 into 01244911c
Pull Request #17324: fix(skills): omit column widths by default in generated grid code

14880 of 17339 branches covered (85.82%)

Branch coverage included in aggregate %.

29947 of 32392 relevant lines covered (92.45%)

34656.81 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 { booleanAttribute, ContentChild, EventEmitter, Output, TemplateRef, inject, ContentChildren, QueryList, ChangeDetectionStrategy } from '@angular/core';
2
import {
3
    Component, Input, ViewChild, OnDestroy, HostBinding
4
} from '@angular/core';
5
import { Subject } from 'rxjs';
6
import {
7
    EntityType,
8
    FieldType,
9
    IExpressionTree,
10
    IQueryBuilderResourceStrings,
11
    QueryBuilderResourceStringsEN,
12
    recreateTree,
13
    getCurrentResourceStrings,
14
    onResourceChangeHandle
15
} from 'igniteui-angular/core';
16
import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component';
17
import { IgxIconService } from 'igniteui-angular/icon';
18
import { editor } from '@igniteui/material-icons-extended';
19
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
20
import { IgxQueryBuilderSearchValueContext } from './query-builder.common';
21
import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component';
22

23
/* wcElementTag: igc-query-builder */
24
/* blazorIndirectRender */
25
/**
26
 * A component used for operating with complex filters by creating or editing conditions
27
 * and grouping them using AND/OR logic.
28
 * It is used internally in the Advanced Filtering of the Grid.
29
 *
30
 * @example
31
 * ```html
32
 * <igx-query-builder [entities]="this.entities">
33
 * </igx-query-builder>
34
 * ```
35
 */
36
@Component({
37
    selector: 'igx-query-builder',
38
    templateUrl: './query-builder.component.html',
39
    changeDetection: ChangeDetectionStrategy.Eager,
40
    imports: [IgxQueryBuilderTreeComponent]
41
})
42
export class IgxQueryBuilderComponent implements OnDestroy {
3✔
43
    protected iconService = inject(IgxIconService);
161✔
44

45
    /**
46
     * @hidden @internal
47
     */
48
    @HostBinding('class.igx-query-builder')
49
    public cssClass = 'igx-query-builder';
161✔
50

51
    /**
52
     * @hidden @internal
53
     */
54
    @HostBinding('style.display')
55
    public display = 'block';
161✔
56

57
    /**
58
     * Gets/sets whether the confirmation dialog should be shown when changing entity.
59
     * Default value is `true`.
60
     */
61
    @Input({ transform: booleanAttribute })
62
    public showEntityChangeDialog = true;
161✔
63

64
    /**
65
     * Gets the list of entities available for the query builder.
66
     *
67
     * Each entity describes a logical group of fields that can be used in queries.
68
     * An entity can optionally have child entities, allowing nested sub-queries.
69
     *
70
     * @returns An array of {@link EntityType} objects.
71
     */
72
    public get entities(): EntityType[] {
73
        return this._entities;
57,804✔
74
    }
75

76
    /**
77
     * Sets the list of entities for the query builder.
78
     * If the `expressionTree` is defined, it will be recreated with the new entities.
79
     *
80
     * Each entity should be an {@link EntityType} object describing the fields and optionally child entities.
81
     *
82
     * Example:
83
     * ```ts
84
     * [
85
     *   {
86
     *     name: 'Orders',
87
     *     fields: [{ field: 'OrderID', dataType: 'number' }],
88
     *     childEntities: [
89
     *       {
90
     *         name: 'OrderDetails',
91
     *         fields: [{ field: 'ProductID', dataType: 'number' }]
92
     *       }
93
     *     ]
94
     *   }
95
     * ]
96
     * ```
97
     *
98
     * @param entities - The array of entities to set.
99
     */
100
    @Input()
101
    public set entities(entities: EntityType[]) {
102
        if (entities !== this._entities) {
161✔
103
            if (entities && this.expressionTree) {
161!
104
                this._expressionTree = recreateTree(this._expressionTree, entities);
×
105
            }
106
        }
107
        this._entities = entities;
161✔
108
    }
109

110
    /**
111
     * Gets the list of fields for the QueryBuilder.
112
     *
113
     * @deprecated since version 19.1.0. Use the `entities` property instead.
114
     * @hidden
115
     */
116
    public get fields(): FieldType[] {
117
        return this._fields;
×
118
    }
119

120
    /**
121
     * Sets the list of fields for the QueryBuilder.
122
     * Automatically wraps them into a single entity to maintain backward compatibility.
123
     *
124
     * @param fields - The array of fields to set.
125
     * @deprecated since version 19.1.0. Use the `entities` property instead.
126
     * @hidden
127
     */
128
    @Input()
129
    public set fields(fields: FieldType[]) {
130
        if (fields) {
×
131
            this._fields = fields;
×
132
            this.entities = [
×
133
                {
134
                    name: null,
135
                    fields: fields
136
                }
137
            ];
138
        }
139
    }
140

141
    /**
142
    * Returns the expression tree.
143
    */
144
    public get expressionTree(): IExpressionTree {
145
        return this._expressionTree;
5,479✔
146
    }
147

148
    /**
149
     * Sets the expression tree.
150
     */
151
    @Input()
152
    public set expressionTree(expressionTree: IExpressionTree) {
153
        if (expressionTree !== this._expressionTree) {
142✔
154
            if (this.entities && expressionTree) {
92✔
155
                this._expressionTree = recreateTree(expressionTree, this.entities);
86✔
156
            } else {
157
                this._expressionTree = expressionTree;
6✔
158
            }
159
        }
160
    }
161

162
    /**
163
     * Gets the `locale` of the query builder.
164
     * If not set, defaults to application's locale.
165
     */
166
    @Input()
167
    public locale: string;
168

169
    /**
170
     * Sets the resource strings.
171
     * By default it uses EN resources.
172
     */
173
    @Input()
174
    public set resourceStrings(value: IQueryBuilderResourceStrings) {
175
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
63✔
176
    }
177

178
    /**
179
     * Returns the resource strings.
180
     */
181
    public get resourceStrings(): IQueryBuilderResourceStrings {
182
        return this._resourceStrings || this._defaultResourceStrings;
5,273✔
183
    }
184

185
    /**
186
     * Disables subsequent entity changes at the root level after the initial selection.
187
     */
188
    @Input()
189
    public disableEntityChange = false;
161✔
190

191
    /**
192
     * Sets/gets the search value template.
193
     */
194
    @Input()
195
    public set searchValueTemplate(template: TemplateRef<IgxQueryBuilderSearchValueContext>) {
196
        this._searchValueTemplate = template;
×
197
    }
198

199
    public get searchValueTemplate(): TemplateRef<IgxQueryBuilderSearchValueContext> {
200
        return this._searchValueTemplate || this.searchValueTemplateDirective?.template;
5,271✔
201
    }
202

203
    /**
204
     * Disables return fields changes at the root level.
205
     */
206
    @Input()
207
    public disableReturnFieldsChange = false;
161✔
208

209
    /**
210
     * Event fired as the expression tree is changed.
211
     *
212
     * ```html
213
     *  <igx-query-builder (expressionTreeChange)='onExpressionTreeChange()'></igx-query-builder>
214
     * ```
215
     */
216
    @Output()
217
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
161✔
218

219
    /**
220
     * @hidden @internal
221
     */
222
    @ContentChild(IgxQueryBuilderSearchValueTemplateDirective)
223
    protected searchValueTemplateDirective: IgxQueryBuilderSearchValueTemplateDirective;
224

225

226

227
    /* contentChildren */
228
    /* blazorInclude */
229
    /* blazorTreatAsCollection */
230
    /* blazorCollectionName: QueryBuilderHeaderCollection */
231
    /* blazorCollectionItemName: QueryBuilderHeader */
232
    /* ngQueryListName: queryBuilderHeaderCollection */
233
    /** @hidden @internal */
234
    @ContentChildren(IgxQueryBuilderHeaderComponent)
235
    protected queryBuilderHeaderCollection: QueryList<IgxQueryBuilderHeaderComponent>;
236

237
    /**
238
     * @hidden @internal
239
     */
240
    @ViewChild(IgxQueryBuilderTreeComponent)
241
    public queryTree: IgxQueryBuilderTreeComponent;
242

243
    private destroy$ = new Subject<any>();
161✔
244
    private _resourceStrings: IQueryBuilderResourceStrings = null;
161✔
245
    private _defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN);
161✔
246
    private _expressionTree: IExpressionTree;
247
    private _fields: FieldType[];
248
    private _entities: EntityType[];
249
    private _shouldEmitTreeChange = true;
161✔
250
    private _searchValueTemplate: TemplateRef<IgxQueryBuilderSearchValueContext>;
251

252
    constructor() {
253
        this.registerSVGIcons();
161✔
254
        onResourceChangeHandle(this.destroy$, () => {
161✔
255
            this._defaultResourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN, false);
4✔
256
        }, this);
257
    }
258

259
    /**
260
     * Returns whether the expression tree can be committed in the current state.
261
     */
262
    public canCommit(): boolean {
263
        return this.queryTree?.canCommitCurrentState() === true;
28✔
264
    }
265

266
    /**
267
     * Commits the expression tree in the current state if it is valid. If not throws an exception.
268
     */
269
    public commit(): void {
270
        if (this.canCommit()) {
3✔
271
            this._shouldEmitTreeChange = false;
2✔
272
            this.queryTree.commitCurrentState();
2✔
273
            this._shouldEmitTreeChange = true;
2✔
274
        } else {
275
            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✔
276
        }
277
    }
278

279
    /**
280
     * Discards all unsaved changes to the expression tree.
281
     */
282
    public discard(): void {
283
        this.queryTree.cancelOperandEdit();
5✔
284
    }
285

286
    /**
287
     * @hidden @internal
288
     */
289
    public ngOnDestroy(): void {
290
        this.destroy$.next(true);
161✔
291
        this.destroy$.complete();
161✔
292
    }
293

294
    /**
295
     * @hidden @internal
296
     *
297
     * used by the grid
298
     */
299
    public get isContextMenuVisible(): boolean {
300
        return this.queryTree.isContextMenuVisible;
×
301
    }
302

303
    /**
304
     * @hidden @internal
305
     *
306
     * used by the grid
307
     */
308
    public exitOperandEdit() {
309
        this.queryTree.exitOperandEdit();
20✔
310
    }
311

312
    /**
313
     * @hidden @internal
314
     *
315
     * used by the grid
316
     */
317
    public setAddButtonFocus() {
318
        this.queryTree.setAddButtonFocus();
53✔
319
    }
320

321
    protected onExpressionTreeChange(tree: IExpressionTree) {
322
        if (tree && this.entities && tree !== this._expressionTree) {
128✔
323
            this._expressionTree = recreateTree(tree, this.entities);
110✔
324
        } else {
325
            this._expressionTree = tree;
18✔
326
        }
327
        if (this._shouldEmitTreeChange) {
128✔
328
            this.expressionTreeChange.emit(tree);
126✔
329
        }
330
    }
331

332
    private registerSVGIcons(): void {
333
        const editorIcons = editor as any[];
161✔
334

335
        editorIcons.forEach((icon) => {
161✔
336
            this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons');
9,660✔
337
            this.iconService.addIconRef(icon.name, 'default', {
9,660✔
338
                name: icon.name,
339
                family: 'imx-icons'
340
            });
341
        });
342

343
        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✔
344
        this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons');
161✔
345
        this.iconService.addIconRef('in', 'default', {
161✔
346
            name: 'in',
347
            family: 'imx-icons'
348
        });
349

350
        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✔
351
        this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons');
161✔
352
        this.iconService.addIconRef('not-in', 'default', {
161✔
353
            name: 'not-in',
354
            family: 'imx-icons'
355
        });
356

357
        this.iconService.addIconRef('add', 'default', {
161✔
358
            name: 'add',
359
            family: 'material',
360
        });
361

362
        this.iconService.addIconRef('close', 'default', {
161✔
363
            name: 'close',
364
            family: 'material',
365
        });
366

367
        this.iconService.addIconRef('check', 'default', {
161✔
368
            name: 'check',
369
            family: 'material',
370
        });
371

372
        this.iconService.addIconRef('delete', 'default', {
161✔
373
            name: 'delete',
374
            family: 'material',
375
        });
376

377
        this.iconService.addIconRef('edit', 'default', {
161✔
378
            name: 'edit',
379
            family: 'material',
380
        });
381
    }
382
}
383

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

© 2026 Coveralls, Inc