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

IgniteUI / igniteui-angular / 23353730325

20 Mar 2026 05:03PM UTC coverage: 9.784% (-79.5%) from 89.264%
23353730325

Pull #17069

github

web-flow
Merge cfa7e86d1 into a4dc50177
Pull Request #17069: fix(IgxGrid): Do not apply width constraint to groups.

921 of 16963 branches covered (5.43%)

Branch coverage included in aggregate %.

1 of 3 new or added lines in 1 file covered. (33.33%)

25213 existing lines in 340 files now uncovered.

3842 of 31721 relevant lines covered (12.11%)

6.13 hits per line

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

1.14
/projects/igniteui-angular/query-builder/src/query-builder/query-builder.component.ts
1
import { booleanAttribute, ContentChild, EventEmitter, Output, TemplateRef, inject, ContentChildren, QueryList } from '@angular/core';
2
import {
3
    Component, Input, ViewChild, ElementRef, 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
    IgxOverlayOutletDirective,
14
    getCurrentResourceStrings,
15
    onResourceChangeHandle
16
} from 'igniteui-angular/core';
17
import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component';
18
import { IgxIconService } from 'igniteui-angular/icon';
19
import { editor } from '@igniteui/material-icons-extended';
20
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
21
import { IgxQueryBuilderSearchValueContext } from './query-builder.common';
22
import { IgxQueryBuilderHeaderComponent } from './query-builder-header.component';
23

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

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

51
    /**
52
     * @hidden @internal
53
     */
54
    @HostBinding('style.display')
UNCOV
55
    public display = 'block';
×
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 })
UNCOV
62
    public showEntityChangeDialog = true;
×
63

64
    /**
65
     * Gets the list of entities available for the IgxQueryBuilderComponent.
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[] {
UNCOV
73
        return this._entities;
×
74
    }
75

76
    /**
77
     * Sets the list of entities for the IgxQueryBuilderComponent.
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[]) {
UNCOV
102
        if (entities !== this._entities) {
×
UNCOV
103
            if (entities && this.expressionTree) {
×
104
                this._expressionTree = recreateTree(this._expressionTree, entities);
×
105
            }
106
        }
UNCOV
107
        this._entities = entities;
×
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 {
UNCOV
145
        return this._expressionTree;
×
146
    }
147

148
    /**
149
     * Sets the expression tree.
150
     */
151
    @Input()
152
    public set expressionTree(expressionTree: IExpressionTree) {
UNCOV
153
        if (expressionTree !== this._expressionTree) {
×
UNCOV
154
            if (this.entities && expressionTree) {
×
UNCOV
155
                this._expressionTree = recreateTree(expressionTree, this.entities);
×
156
            } else {
UNCOV
157
                this._expressionTree = expressionTree;
×
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) {
UNCOV
175
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
×
176
    }
177

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

185
    /**
186
     * Disables subsequent entity changes at the root level after the initial selection.
187
     */
188
    @Input()
UNCOV
189
    public disableEntityChange = false;
×
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> {
UNCOV
200
        return this._searchValueTemplate || this.searchValueTemplateDirective?.template;
×
201
    }
202

203
    /**
204
     * Disables return fields changes at the root level.
205
     */
206
    @Input()
UNCOV
207
    public disableReturnFieldsChange = false;
×
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()
UNCOV
217
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
×
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

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

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

259
    /**
260
     * Returns whether the expression tree can be committed in the current state.
261
     */
262
    public canCommit(): boolean {
UNCOV
263
        return this.queryTree?.canCommitCurrentState() === true;
×
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 {
UNCOV
270
        if (this.canCommit()) {
×
UNCOV
271
            this._shouldEmitTreeChange = false;
×
UNCOV
272
            this.queryTree.commitCurrentState();
×
UNCOV
273
            this._shouldEmitTreeChange = true;
×
274
        } else {
UNCOV
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.');
×
276
        }
277
    }
278

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

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

294
    /**
295
     * @hidden @internal
296
     *
297
     * used by the grid
298
     */
299
    public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) {
UNCOV
300
        this.queryTree.setPickerOutlet(outlet);
×
301
    }
302

303
    /**
304
     * @hidden @internal
305
     *
306
     * used by the grid
307
     */
308
    public get isContextMenuVisible(): boolean {
309
        return this.queryTree.isContextMenuVisible;
×
310
    }
311

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

321
    /**
322
     * @hidden @internal
323
     *
324
     * used by the grid
325
     */
326
    public setAddButtonFocus() {
UNCOV
327
        this.queryTree.setAddButtonFocus();
×
328
    }
329

330
    protected onExpressionTreeChange(tree: IExpressionTree) {
UNCOV
331
        if (tree && this.entities && tree !== this._expressionTree) {
×
UNCOV
332
            this._expressionTree = recreateTree(tree, this.entities);
×
333
        } else {
UNCOV
334
            this._expressionTree = tree;
×
335
        }
UNCOV
336
        if (this._shouldEmitTreeChange) {
×
UNCOV
337
            this.expressionTreeChange.emit(tree);
×
338
        }
339
    }
340

341
    private registerSVGIcons(): void {
UNCOV
342
        const editorIcons = editor as any[];
×
343

UNCOV
344
        editorIcons.forEach((icon) => {
×
UNCOV
345
            this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons');
×
UNCOV
346
            this.iconService.addIconRef(icon.name, 'default', {
×
347
                name: icon.name,
348
                family: 'imx-icons'
349
            });
350
        });
351

UNCOV
352
        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>';
×
UNCOV
353
        this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons');
×
UNCOV
354
        this.iconService.addIconRef('in', 'default', {
×
355
            name: 'in',
356
            family: 'imx-icons'
357
        });
358

UNCOV
359
        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>';
×
UNCOV
360
        this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons');
×
UNCOV
361
        this.iconService.addIconRef('not-in', 'default', {
×
362
            name: 'not-in',
363
            family: 'imx-icons'
364
        });
365

UNCOV
366
        this.iconService.addIconRef('add', 'default', {
×
367
            name: 'add',
368
            family: 'material',
369
        });
370

UNCOV
371
        this.iconService.addIconRef('close', 'default', {
×
372
            name: 'close',
373
            family: 'material',
374
        });
375

UNCOV
376
        this.iconService.addIconRef('check', 'default', {
×
377
            name: 'check',
378
            family: 'material',
379
        });
380

UNCOV
381
        this.iconService.addIconRef('delete', 'default', {
×
382
            name: 'delete',
383
            family: 'material',
384
        });
385

UNCOV
386
        this.iconService.addIconRef('edit', 'default', {
×
387
            name: 'edit',
388
            family: 'material',
389
        });
390
    }
391
}
392

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