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

IgniteUI / igniteui-angular / 17127554528

21 Aug 2025 12:56PM UTC coverage: 91.522% (+0.009%) from 91.513%
17127554528

push

github

web-flow
Merge pull request #16135 from IgniteUI/rivanova/fix-16057-20.0.x

fix(column): use minWidth when sizing columns - 20.0.x

13517 of 15844 branches covered (85.31%)

14 of 14 new or added lines in 2 files covered. (100.0%)

5 existing lines in 1 file now uncovered.

27237 of 29760 relevant lines covered (91.52%)

34639.71 hits per line

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

90.16
/projects/igniteui-angular/src/lib/query-builder/query-builder.component.ts
1
import { booleanAttribute, ContentChild, EventEmitter, Output, TemplateRef } from '@angular/core';
2
import {
3
    Component, Input, ViewChild, ElementRef, OnDestroy, HostBinding
4
} from '@angular/core';
5
import { Subject } from 'rxjs';
6
import { IQueryBuilderResourceStrings, QueryBuilderResourceStringsEN } from '../core/i18n/query-builder-resources';
7
import { IExpressionTree } from '../data-operations/filtering-expressions-tree';
8
import { IgxOverlayOutletDirective } from '../directives/toggle/toggle.directive';
9
import { EntityType, FieldType } from '../grids/common/grid.interface';
10
import { getCurrentResourceStrings } from '../core/i18n/resources';
11
import { IgxQueryBuilderTreeComponent } from './query-builder-tree.component';
12
import { IgxIconService } from '../icon/icon.service';
13
import { editor } from '@igniteui/material-icons-extended';
14
import { IgxQueryBuilderSearchValueTemplateDirective } from './query-builder.directives';
15
import { recreateTree } from '../data-operations/expressions-tree-util';
16

17
/**
18
 * A component used for operating with complex filters by creating or editing conditions
19
 * and grouping them using AND/OR logic.
20
 * It is used internally in the Advanced Filtering of the Grid.
21
 *
22
 * @example
23
 * ```html
24
 * <igx-query-builder [entities]="this.entities">
25
 * </igx-query-builder>
26
 * ```
27
 */
28
@Component({
29
    selector: 'igx-query-builder',
30
    templateUrl: './query-builder.component.html',
31
    imports: [IgxQueryBuilderTreeComponent]
32
})
33
export class IgxQueryBuilderComponent implements OnDestroy {
3✔
34
    /**
35
     * @hidden @internal
36
     */
37
    @HostBinding('class.igx-query-builder')
38
    public cssClass = 'igx-query-builder';
161✔
39

40
    /**
41
     * @hidden @internal
42
     */
43
    @HostBinding('style.display')
44
    public display = 'block';
161✔
45

46
    /**
47
     * Gets/sets whether the confirmation dialog should be shown when changing entity.
48
     * Default value is `true`.
49
     */
50
    @Input({ transform: booleanAttribute })
51
    public showEntityChangeDialog = true;
161✔
52

53
    /**
54
     * Gets the list of entities available for the IgxQueryBuilderComponent.
55
     *
56
     * Each entity describes a logical group of fields that can be used in queries.
57
     * An entity can optionally have child entities, allowing nested sub-queries.
58
     *
59
     * @returns An array of {@link EntityType} objects.
60
     */
61
    public get entities(): EntityType[] {
62
        return this._entities;
57,804✔
63
    }
64

65
    /**
66
     * Sets the list of entities for the IgxQueryBuilderComponent.
67
     * If the `expressionTree` is defined, it will be recreated with the new entities.
68
     *
69
     * Each entity should be an {@link EntityType} object describing the fields and optionally child entities.
70
     *
71
     * Example:
72
     * ```ts
73
     * [
74
     *   {
75
     *     name: 'Orders',
76
     *     fields: [{ field: 'OrderID', dataType: 'number' }],
77
     *     childEntities: [
78
     *       {
79
     *         name: 'OrderDetails',
80
     *         fields: [{ field: 'ProductID', dataType: 'number' }]
81
     *       }
82
     *     ]
83
     *   }
84
     * ]
85
     * ```
86
     *
87
     * @param entities - The array of entities to set.
88
     */
89
    @Input()
90
    public set entities(entities: EntityType[]) {
91
        if (entities !== this._entities) {
161✔
92
            if (entities && this.expressionTree) {
161!
93
                this._expressionTree = recreateTree(this._expressionTree, entities);
×
94
            }
95
        }
96
        this._entities = entities;
161✔
97
    }
98

99
    /**
100
     * Gets the list of fields for the QueryBuilder.
101
     *
102
     * @deprecated since version 19.1.0. Use the `entities` property instead.
103
     * @hidden
104
     */
105
    public get fields(): FieldType[] {
UNCOV
106
        return this._fields;
×
107
    }
108

109
    /**
110
     * Sets the list of fields for the QueryBuilder.
111
     * Automatically wraps them into a single entity to maintain backward compatibility.
112
     *
113
     * @param fields - The array of fields to set.
114
     * @deprecated since version 19.1.0. Use the `entities` property instead.
115
     * @hidden
116
     */
117
    @Input()
118
    public set fields(fields: FieldType[]) {
UNCOV
119
        if (fields) {
×
UNCOV
120
            this._fields = fields;
×
UNCOV
121
            this.entities = [
×
122
                {
123
                    name: null,
124
                    fields: fields
125
                }
126
            ];
127
        }
128
    }
129

130
    /**
131
    * Returns the expression tree.
132
    */
133
    public get expressionTree(): IExpressionTree {
134
        return this._expressionTree;
5,479✔
135
    }
136

137
    /**
138
     * Sets the expression tree.
139
     */
140
    @Input()
141
    public set expressionTree(expressionTree: IExpressionTree) {
142
        if (expressionTree !== this._expressionTree) {
142✔
143
            if (this.entities && expressionTree) {
92✔
144
                this._expressionTree = recreateTree(expressionTree, this.entities);
86✔
145
            } else {
146
                this._expressionTree = expressionTree;
6✔
147
            }
148
        }
149
    }
150

151
    /**
152
     * Gets the `locale` of the query builder.
153
     * If not set, defaults to application's locale.
154
     */
155
    @Input()
156
    public locale: string;
157

158
    /**
159
     * Sets the resource strings.
160
     * By default it uses EN resources.
161
     */
162
    @Input()
163
    public set resourceStrings(value: IQueryBuilderResourceStrings) {
164
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
1✔
165
    }
166

167
    /**
168
     * Returns the resource strings.
169
     */
170
    public get resourceStrings(): IQueryBuilderResourceStrings {
171
        return this._resourceStrings;
5,272✔
172
    }
173

174
    /**
175
     * Disables subsequent entity changes at the root level after the initial selection.
176
     */
177
    @Input()
178
    public disableEntityChange = false;
161✔
179

180
    /**
181
     * Disables return fields changes at the root level.
182
     */
183
     @Input()
184
     public disableReturnFieldsChange = false;
161✔
185

186
    /**
187
     * Event fired as the expression tree is changed.
188
     *
189
     * ```html
190
     *  <igx-query-builder (expressionTreeChange)='onExpressionTreeChange()'></igx-query-builder>
191
     * ```
192
     */
193
    @Output()
194
    public expressionTreeChange = new EventEmitter<IExpressionTree>();
161✔
195

196
    /**
197
     * @hidden @internal
198
     */
199
    @ContentChild(IgxQueryBuilderSearchValueTemplateDirective, { read: TemplateRef })
200
    public searchValueTemplate: TemplateRef<any>;
201

202
    /**
203
     * @hidden @internal
204
     */
205
    @ViewChild(IgxQueryBuilderTreeComponent)
206
    public queryTree: IgxQueryBuilderTreeComponent;
207

208
    private destroy$ = new Subject<any>();
161✔
209
    private _resourceStrings = getCurrentResourceStrings(QueryBuilderResourceStringsEN);
161✔
210
    private _expressionTree: IExpressionTree;
211
    private _fields: FieldType[];
212
    private _entities: EntityType[];
213
    private _shouldEmitTreeChange = true;
161✔
214

215
    constructor(protected iconService: IgxIconService) {
161✔
216
        this.registerSVGIcons();
161✔
217
    }
218

219
    /**
220
     * Returns whether the expression tree can be committed in the current state.
221
     */
222
    public canCommit(): boolean {
223
        return this.queryTree?.canCommitCurrentState() === true;
28✔
224
    }
225

226
    /**
227
     * Commits the expression tree in the current state if it is valid. If not throws an exception.
228
     */
229
    public commit(): void {
230
        if (this.canCommit()) {
3✔
231
            this._shouldEmitTreeChange = false;
2✔
232
            this.queryTree.commitCurrentState();
2✔
233
            this._shouldEmitTreeChange = true;
2✔
234
        } else {
235
            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✔
236
        }
237
    }
238

239
    /**
240
     * Discards all unsaved changes to the expression tree.
241
     */
242
    public discard(): void {
243
        this.queryTree.cancelOperandEdit();
5✔
244
    }
245

246
    /**
247
     * @hidden @internal
248
     */
249
    public ngOnDestroy(): void {
250
        this.destroy$.next(true);
161✔
251
        this.destroy$.complete();
161✔
252
    }
253

254
    /**
255
     * @hidden @internal
256
     *
257
     * used by the grid
258
     */
259
    public setPickerOutlet(outlet?: IgxOverlayOutletDirective | ElementRef) {
260
        this.queryTree.setPickerOutlet(outlet);
59✔
261
    }
262

263
    /**
264
     * @hidden @internal
265
     *
266
     * used by the grid
267
     */
268
    public get isContextMenuVisible(): boolean {
UNCOV
269
        return this.queryTree.isContextMenuVisible;
×
270
    }
271

272
    /**
273
     * @hidden @internal
274
     *
275
     * used by the grid
276
     */
277
    public exitOperandEdit() {
278
        this.queryTree.exitOperandEdit();
20✔
279
    }
280

281
    /**
282
     * @hidden @internal
283
     *
284
     * used by the grid
285
     */
286
    public setAddButtonFocus() {
287
        this.queryTree.setAddButtonFocus();
53✔
288
    }
289

290
    public onExpressionTreeChange(tree: IExpressionTree) {
291
        if (tree && this.entities && tree !== this._expressionTree) {
128✔
292
            this._expressionTree = recreateTree(tree, this.entities);
110✔
293
        } else {
294
            this._expressionTree = tree;
18✔
295
        }
296
        if (this._shouldEmitTreeChange) {
128✔
297
            this.expressionTreeChange.emit(tree);
126✔
298
        }
299
    }
300

301
    private registerSVGIcons(): void {
302
        const editorIcons = editor as any[];
161✔
303

304
        editorIcons.forEach((icon) => {
161✔
305
            this.iconService.addSvgIconFromText(icon.name, icon.value, 'imx-icons');
9,660✔
306
            this.iconService.addIconRef(icon.name, 'default', {
9,660✔
307
                name: icon.name,
308
                family: 'imx-icons'
309
            });
310
        });
311

312
        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✔
313
        this.iconService.addSvgIconFromText('in', inIcon, 'imx-icons');
161✔
314
        this.iconService.addIconRef('in', 'default', {
161✔
315
            name: 'in',
316
            family: 'imx-icons'
317
        });
318

319
        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✔
320
        this.iconService.addSvgIconFromText('not-in', notInIcon, 'imx-icons');
161✔
321
        this.iconService.addIconRef('not-in', 'default', {
161✔
322
            name: 'not-in',
323
            family: 'imx-icons'
324
        });
325

326
        this.iconService.addIconRef('add', 'default', {
161✔
327
            name: 'add',
328
            family: 'material',
329
        });
330

331
        this.iconService.addIconRef('close', 'default', {
161✔
332
            name: 'close',
333
            family: 'material',
334
        });
335

336
        this.iconService.addIconRef('check', 'default', {
161✔
337
            name: 'check',
338
            family: 'material',
339
        });
340

341
        this.iconService.addIconRef('delete', 'default', {
161✔
342
            name: 'delete',
343
            family: 'material',
344
        });
345

346
        this.iconService.addIconRef('edit', 'default', {
161✔
347
            name: 'edit',
348
            family: 'material',
349
        });
350
    }
351
}
352

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