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

IgniteUI / igniteui-angular / 26023601418

18 May 2026 08:57AM UTC coverage: 4.854% (-85.3%) from 90.174%
26023601418

Pull #17281

github

web-flow
Merge e7ce7a18e into 5a85df190
Pull Request #17281: feat: Added virtual scroll component and sample implementation

400 of 17347 branches covered (2.31%)

Branch coverage included in aggregate %.

63 of 222 new or added lines in 4 files covered. (28.38%)

27932 existing lines in 341 files now uncovered.

2022 of 32547 relevant lines covered (6.21%)

0.72 hits per line

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

1.15
/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, 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
    imports: [IgxQueryBuilderTreeComponent]
40
})
41
export class IgxQueryBuilderComponent implements OnDestroy {
3✔
UNCOV
42
    protected iconService = inject(IgxIconService);
×
43

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

50
    /**
51
     * @hidden @internal
52
     */
53
    @HostBinding('style.display')
UNCOV
54
    public display = 'block';
×
55

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

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

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

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

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

140
    /**
141
    * Returns the expression tree.
142
    */
143
    public get expressionTree(): IExpressionTree {
UNCOV
144
        return this._expressionTree;
×
145
    }
146

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

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

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

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

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

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

198
    public get searchValueTemplate(): TemplateRef<IgxQueryBuilderSearchValueContext> {
UNCOV
199
        return this._searchValueTemplate || this.searchValueTemplateDirective?.template;
×
200
    }
201

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

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

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

224

225

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

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

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

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

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

265
    /**
266
     * Commits the expression tree in the current state if it is valid. If not throws an exception.
267
     */
268
    public commit(): void {
UNCOV
269
        if (this.canCommit()) {
×
UNCOV
270
            this._shouldEmitTreeChange = false;
×
UNCOV
271
            this.queryTree.commitCurrentState();
×
UNCOV
272
            this._shouldEmitTreeChange = true;
×
273
        } else {
UNCOV
274
            throw new Error('Expression tree can\'t be committed in the current state. Use `canCommit` method to check if the current state is valid.');
×
275
        }
276
    }
277

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

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

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

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

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

320
    protected onExpressionTreeChange(tree: IExpressionTree) {
UNCOV
321
        if (tree && this.entities && tree !== this._expressionTree) {
×
UNCOV
322
            this._expressionTree = recreateTree(tree, this.entities);
×
323
        } else {
UNCOV
324
            this._expressionTree = tree;
×
325
        }
UNCOV
326
        if (this._shouldEmitTreeChange) {
×
UNCOV
327
            this.expressionTreeChange.emit(tree);
×
328
        }
329
    }
330

331
    private registerSVGIcons(): void {
UNCOV
332
        const editorIcons = editor as any[];
×
333

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

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

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

UNCOV
356
        this.iconService.addIconRef('add', 'default', {
×
357
            name: 'add',
358
            family: 'material',
359
        });
360

UNCOV
361
        this.iconService.addIconRef('close', 'default', {
×
362
            name: 'close',
363
            family: 'material',
364
        });
365

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

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

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

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