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

IgniteUI / igniteui-angular / 33847475085

04 Sep 2026 07:09AM UTC coverage: 90.089% (+0.03%) from 90.064%
33847475085

push

github

web-flow
fix(grid): correct Excel filter virtual sizing (#17524)

Co-authored-by: Stamen Stoychev <chronos.stz@gmail.com>

15090 of 17609 branches covered (85.69%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

286 existing lines in 17 files now uncovered.

30285 of 32758 relevant lines covered (92.45%)

38346.41 hits per line

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

93.93
/projects/igniteui-angular/tree/src/tree/tree-node/tree-node.component.ts
1
import {
2
    ChangeDetectorRef,
3
    Component,
4
    ContentChildren,
5
    Directive,
6
    ElementRef,
7
    EventEmitter,
8
    HostBinding,
9
    HostListener,
10
    Input,
11
    OnDestroy,
12
    OnInit,
13
    Output,
14
    QueryList,
15
    TemplateRef,
16
    ViewChild,
17
    booleanAttribute,
18
    inject,
19
    ChangeDetectionStrategy,
20
    ViewEncapsulation,
21
} from '@angular/core';
22
import { takeUntil } from 'rxjs/operators';
23
import {
24
    IgxTree,
25
    IgxTreeNode,
26
    IgxTreeSelectionType,
27
    IGX_TREE_COMPONENT,
28
    IGX_TREE_NODE_COMPONENT,
29
    ITreeNodeTogglingEventArgs
30
} from '../common';
31
import { IgxTreeNavigationService } from '../tree-navigation.service';
32
import { IgxTreeSelectionService } from '../tree-selection.service';
33
import { IgxTreeService } from '../tree.service';
34
import { NgTemplateOutlet, NgClass } from '@angular/common';
35
import { IgxIconComponent } from 'igniteui-angular/icon';
36
import { IgxCheckboxComponent } from 'igniteui-angular/checkbox';
37
import { IgxCircularProgressBarComponent } from 'igniteui-angular/progressbar';
38
import { ToggleAnimationPlayer, ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';
39
import { getCurrentResourceStrings, onResourceChangeHandle, ITreeResourceStrings, TreeResourceStringsEN } from 'igniteui-angular/core';
40

41
// TODO: Implement aria functionality
42
/**
43
 * @hidden @internal
44
 * Used for links (`a` tags) in the body of an `igx-tree-node`. Handles aria and event dispatch.
45
 */
46
@Directive({
47
    selector: `[igxTreeNodeLink]`,
48
    standalone: true
49
})
50
export class IgxTreeNodeLinkDirective implements OnDestroy {
3✔
51
    private node = inject<IgxTreeNode<any>>(IGX_TREE_NODE_COMPONENT, { optional: true });
30✔
52
    private navService = inject(IgxTreeNavigationService);
30✔
53
    public elementRef = inject(ElementRef);
30✔
54

55

56
    @HostBinding('attr.role')
57
    public role = 'treeitem';
30✔
58

59
    /**
60
     * The node's parent. Should be used only when the link is defined
61
     * in `<ng-template>` tag outside of its parent, as Angular DI will not properly provide a reference
62
     *
63
     * ```html
64
     * <igx-tree>
65
     *     <igx-tree-node #myNode *ngFor="let node of data" [data]="node">
66
     *         <ng-template *ngTemplateOutlet="nodeTemplate; context: { $implicit: data, parentNode: myNode }">
67
     *         </ng-template>
68
     *     </igx-tree-node>
69
     *     ...
70
     *     <!-- node template is defined under tree to access related services -->
71
     *     <ng-template #nodeTemplate let-data let-node="parentNode">
72
     *         <a [igxTreeNodeLink]="node">{{ data.label }}</a>
73
     *     </ng-template>
74
     * </igx-tree>
75
     * ```
76
     */
77
    @Input('igxTreeNodeLink')
78
    public set parentNode(val: any) {
79
        if (val) {
30✔
80
            this._parentNode = val;
15✔
81
            (this._parentNode as any).addLinkChild(this);
15✔
82
        }
83
    }
84

85
    public get parentNode(): any {
86
        return this._parentNode;
113✔
87
    }
88

89
    /** A pointer to the parent node */
90
    private get target(): IgxTreeNode<any> {
91
        return this.node || this.parentNode;
226✔
92
    }
93

94
    private _parentNode: IgxTreeNode<any> = null!;
30✔
95

96
    /** @hidden @internal */
97
    @HostBinding('attr.tabindex')
98
    public get tabIndex(): number {
99
        return this.navService.focusedNode === this.target ? (this.target?.disabled ? -1 : 0) : -1;
181!
100
    }
101

102
    /**
103
     * @hidden @internal
104
     * Clear the node's focused state
105
     */
106
    @HostListener('blur')
107
    public handleBlur() {
108
        this.target.isFocused = false;
×
109
    }
110

111
    /**
112
     * @hidden @internal
113
     * Set the node as focused
114
     */
115
    @HostListener('focus')
116
    public handleFocus() {
117
        if (this.target && !this.target.disabled) {
2✔
118
            if (this.navService.focusedNode !== this.target) {
2!
119
                this.navService.focusedNode = this.target;
×
120
            }
121
            this.target.isFocused = true;
2✔
122
        }
123
    }
124

125
    public ngOnDestroy() {
126
        this.target.removeLinkChild(this);
30✔
127
    }
128
}
129

130
/**
131
 *
132
 * The tree node component represents a child node of the tree component or another tree node.
133
 * Usage:
134
 *
135
 * ```html
136
 *  <igx-tree>
137
 *  ...
138
 *    <igx-tree-node [data]="data" [selected]="service.isNodeSelected(data.Key)" [expanded]="service.isNodeExpanded(data.Key)">
139
 *      {{ data.FirstName }} {{ data.LastName }}
140
 *    </igx-tree-node>
141
 *  ...
142
 *  </igx-tree>
143
 * ```
144
 */
145
@Component({
146
    selector: 'igx-tree-node',
147
    templateUrl: 'tree-node.component.html',
148
    styleUrl: 'tree-node.component.css',
149
    encapsulation: ViewEncapsulation.None,
150
    providers: [
151
        { provide: IGX_TREE_NODE_COMPONENT, useExisting: IgxTreeNodeComponent }
152
    ],
153
    changeDetection: ChangeDetectionStrategy.Eager,
154
    imports: [NgTemplateOutlet, IgxIconComponent, IgxCheckboxComponent, NgClass, IgxCircularProgressBarComponent]
155
})
156
export class IgxTreeNodeComponent<T> extends ToggleAnimationPlayer implements IgxTreeNode<T>, OnInit, OnDestroy {
3✔
157
    public tree = inject<IgxTree>(IGX_TREE_COMPONENT);
3,854✔
158
    protected selectionService = inject(IgxTreeSelectionService);
3,854✔
159
    protected treeService = inject(IgxTreeService);
3,854✔
160
    protected navService = inject(IgxTreeNavigationService);
3,854✔
161
    protected cdr = inject(ChangeDetectorRef);
3,854✔
162
    private element = inject<ElementRef<HTMLElement>>(ElementRef);
3,854✔
163
    public parentNode = inject<IgxTreeNode<any>>(IGX_TREE_NODE_COMPONENT, { optional: true, skipSelf: true });
3,854✔
164

165
    /**
166
     * The data entry that the node is visualizing.
167
     *
168
     * @remarks
169
     * Required for searching through nodes.
170
     *
171
     * @example
172
     * ```html
173
     *  <igx-tree>
174
     *  ...
175
     *    <igx-tree-node [data]="data">
176
     *      {{ data.FirstName }} {{ data.LastName }}
177
     *    </igx-tree-node>
178
     *  ...
179
     *  </igx-tree>
180
     * ```
181
     */
182
    @Input()
183
    public data!: T;
184

185
    /**
186
     * To be used for load-on-demand scenarios in order to specify whether the node is loading data.
187
     *
188
     * @remarks
189
     * Loading nodes do not render children.
190
     */
191
    @Input({ transform: booleanAttribute })
192
    public loading = false;
3,854✔
193

194
    // TO DO: return different tab index depending on anchor child
195
    /** @hidden @internal */
196
    public set tabIndex(val: number) {
197
        this._tabIndex = val;
73✔
198
    }
199

200
    /** @hidden @internal */
201
    public get tabIndex(): number {
202
        if (this.disabled) {
39,921✔
203
            return -1;
736✔
204
        }
205
        if (this._tabIndex === null) {
39,185✔
206
            if (this.navService.focusedNode === null) {
38,825✔
207
                return this.hasLinkChildren ? -1 : 0;
34,278✔
208
            }
209
            return -1;
4,547✔
210
        }
211
        return this.hasLinkChildren ? -1 : this._tabIndex;
360✔
212
    }
213

214
    /** @hidden @internal */
215
    public override get animationSettings(): ToggleAnimationSettings {
216
        return this.tree.animationSettings;
25✔
217
    }
218

219
    /**
220
     * Gets/Sets the resource strings.
221
     *
222
     * @remarks
223
     * Uses EN resources by default.
224
     */
225
    @Input()
226
    public set resourceStrings(value: ITreeResourceStrings) {
227
        this._resourceStrings = value;
2✔
228
        this._customResourceStrings = Object.assign({}, this._defaultResourceStrings, this._resourceStrings);
2✔
229
    }
230

231
    /**
232
     * An accessor that returns the resource strings.
233
     */
234
    public get resourceStrings(): ITreeResourceStrings {
235
        return this._resourceStrings ? this._customResourceStrings : this._defaultResourceStrings;
23,563✔
236
    }
237

238
    /**
239
     * Gets/Sets the active state of the node
240
     *
241
     * @param value: boolean
242
     */
243
    @Input({ transform: booleanAttribute })
244
    public set active(value: boolean) {
245
        if (value) {
247✔
246
            this.navService.activeNode = this;
175✔
247
            this.tree.activeNodeBindingChange.emit(this);
175✔
248
        }
249
    }
250

251
    public get active(): boolean {
252
        return this.navService.activeNode === this;
39,922✔
253
    }
254

255
    /**
256
     * Emitted when the node's `selected` property changes.
257
     *
258
     * ```html
259
     * <igx-tree>
260
     *      <igx-tree-node *ngFor="let node of data" [data]="node" [(selected)]="node.selected">
261
     *      </igx-tree-node>
262
     * </igx-tree>
263
     * ```
264
     *
265
     * ```typescript
266
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
267
     * node.selectedChange.pipe(takeUntil(this.destroy$)).subscribe((e: boolean) => console.log("Node selection changed to ", e))
268
     * ```
269
     */
270
    @Output()
271
    public selectedChange = new EventEmitter<boolean>();
3,854✔
272

273
    /**
274
     * Emitted when the node's `expanded` property changes.
275
     *
276
     * ```html
277
     * <igx-tree>
278
     *      <igx-tree-node *ngFor="let node of data" [data]="node" [(expanded)]="node.expanded">
279
     *      </igx-tree-node>
280
     * </igx-tree>
281
     * ```
282
     *
283
     * ```typescript
284
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
285
     * node.expandedChange.pipe(takeUntil(this.destroy$)).subscribe((e: boolean) => console.log("Node expansion state changed to ", e))
286
     * ```
287
     */
288
    @Output()
289
    public expandedChange = new EventEmitter<boolean>();
3,854✔
290

291
    /** @hidden @internal */
292
    public get focused() {
293
        return this.isFocused &&
39,921✔
294
            this.navService.focusedNode === this;
295
    }
296

297
    /**
298
     * Retrieves the full path to the node incuding itself
299
     *
300
     * ```typescript
301
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
302
     * const path: IgxTreeNode<any>[] = node.path;
303
     * ```
304
     */
305
    public get path(): IgxTreeNode<any>[] {
306
        return this.parentNode?.path ? [...this.parentNode.path, this] : [this];
719✔
307
    }
308

309
    // TODO: bind to disabled state when node is dragged
310
    /**
311
     * Gets/Sets the disabled state of the node
312
     *
313
     * @param value: boolean
314
     */
315
    @Input({ transform: booleanAttribute })
316
    @HostBinding('class.igx-tree-node--disabled')
317
    public get disabled(): boolean {
318
        return this._disabled;
127,506✔
319
    }
320

321
    public set disabled(value: boolean) {
322
        if (value !== this._disabled) {
398✔
323
            this._disabled = value;
83✔
324
            this.tree.disabledChange.emit(this);
83✔
325
        }
326
    }
327

328
    /** @hidden @internal */
329
    @HostBinding('class.igx-tree-node')
330
    public cssClass = 'igx-tree-node';
3,854✔
331

332
    /** @hidden @internal */
333
    @HostBinding('attr.role')
334
    public get role() {
335
        return this.hasLinkChildren ? 'none' : 'treeitem';
79,819✔
336
    }
337

338
    /** @hidden @internal */
339
    @ContentChildren(IgxTreeNodeLinkDirective, { read: ElementRef })
340
    public linkChildren!: QueryList<ElementRef>;
341

342
    /** @hidden @internal */
343
    @ContentChildren(IGX_TREE_NODE_COMPONENT, { read: IGX_TREE_NODE_COMPONENT })
344
    public _children!: QueryList<IgxTreeNode<any>>;
345

346
    /** @hidden @internal */
347
    @ContentChildren(IGX_TREE_NODE_COMPONENT, { read: IGX_TREE_NODE_COMPONENT, descendants: true })
348
    public allChildren!: QueryList<IgxTreeNode<any>>;
349

350
    /**
351
     * Return the child nodes of the node (if any)
352
     *
353
     * @remarks
354
     * Returns `null` if node does not have children
355
     *
356
     * @example
357
     * ```typescript
358
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
359
     * const children: IgxTreeNode<any>[] = node.children;
360
     * ```
361
     */
362
    public get children(): IgxTreeNode<any>[] {
UNCOV
363
        return this._children?.length ? this._children.toArray() : null!;
×
364
    }
365

366
    // TODO: will be used in Drag and Drop implementation
367
    /** @hidden @internal */
368
    @ViewChild('ghostTemplate', { read: ElementRef })
369
    public header!: ElementRef;
370

371
    @ViewChild('defaultIndicator', { read: TemplateRef, static: true })
372
    private _defaultExpandIndicatorTemplate!: TemplateRef<any>;
373

374
    @ViewChild('childrenContainer', { read: ElementRef })
375
    private childrenContainer!: ElementRef;
376

377
    private get hasLinkChildren(): boolean {
378
        return this.linkChildren?.length > 0 || this.registeredChildren?.length > 0;
114,457✔
379
    }
380

381
    /** @hidden @internal */
382
    public isFocused!: boolean;
383

384
    /** @hidden @internal */
385
    public registeredChildren: IgxTreeNodeLinkDirective[] = [];
3,854✔
386

387
    private _resourceStrings: ITreeResourceStrings = null!;
3,854✔
388
    private _customResourceStrings: ITreeResourceStrings = null!;
3,854✔
389
    private _defaultResourceStrings = getCurrentResourceStrings(TreeResourceStringsEN);
3,854✔
390
    private _tabIndex: number | null = null;
3,854✔
391
    private _disabled = false;
3,854✔
392

393
    constructor() {
394
        super();
3,854✔
395
        onResourceChangeHandle(this.destroy$, () => {
3,854✔
396
            this._defaultResourceStrings = getCurrentResourceStrings(TreeResourceStringsEN, false);
310✔
397
            this._customResourceStrings = this._resourceStrings ? Object.assign({}, this._defaultResourceStrings, this._resourceStrings) : null!;
310✔
398
        }, this);
399
    }
400

401
    /**
402
     * @hidden @internal
403
     */
404
    public get showSelectors() {
405
        return this.tree.selection !== IgxTreeSelectionType.None;
39,921✔
406
    }
407

408
    /**
409
     * @hidden @internal
410
     */
411
    public get indeterminate(): boolean {
412
        return this.selectionService.isNodeIndeterminate(this);
7,736✔
413
    }
414

415
    /** The depth of the node, relative to the root
416
     *
417
     * ```html
418
     * <igx-tree>
419
     *  ...
420
     *  <igx-tree-node #node>
421
     *      My level is {{ node.level }}
422
     *  </igx-tree-node>
423
     * </igx-tree>
424
     * ```
425
     *
426
     * ```typescript
427
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[12])[0];
428
     * const level: number = node.level;
429
     * ```
430
     */
431
    public get level(): number {
432
        return this.parentNode ? this.parentNode.level + 1 : 0;
205,193✔
433
    }
434

435
    /** Get/set whether the node is selected. Supporst two-way binding.
436
     *
437
     * ```html
438
     * <igx-tree>
439
     *  ...
440
     *  <igx-tree-node *ngFor="let node of data" [(selected)]="node.selected">
441
     *      {{ node.label }}
442
     *  </igx-tree-node>
443
     * </igx-tree>
444
     * ```
445
     *
446
     * ```typescript
447
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
448
     * const selected = node.selected;
449
     * node.selected = true;
450
     * ```
451
     */
452
    @Input({ transform: booleanAttribute })
453
    public get selected(): boolean {
454
        return this.selectionService.isNodeSelected(this);
48,379✔
455
    }
456

457
    public set selected(val: boolean) {
458
        if (!(this.tree?.nodes && this.tree.nodes.find((e) => e === this)) && val) {
5,498✔
459
            this.tree.forceSelect.push(this);
358✔
460
            return;
358✔
461
        }
462
        if (val && !this.selectionService.isNodeSelected(this)) {
882✔
463
            this.selectionService.selectNodesWithNoEvent([this]);
99✔
464
        }
465
        if (!val && this.selectionService.isNodeSelected(this)) {
882✔
466
            this.selectionService.deselectNodesWithNoEvent([this]);
9✔
467
        }
468
    }
469

470
    /** Get/set whether the node is expanded
471
     *
472
     * ```html
473
     * <igx-tree>
474
     *  ...
475
     *  <igx-tree-node *ngFor="let node of data" [expanded]="node.name === this.expandedNode">
476
     *      {{ node.label }}
477
     *  </igx-tree-node>
478
     * </igx-tree>
479
     * ```
480
     *
481
     * ```typescript
482
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
483
     * const expanded = node.expanded;
484
     * node.expanded = true;
485
     * ```
486
     */
487
    @Input({ transform: booleanAttribute })
488
    public get expanded() {
489
        return this.treeService.isExpanded(this);
127,977✔
490
    }
491

492
    public set expanded(val: boolean) {
493
        if (val) {
1,979✔
494
            this.treeService.expand(this, false);
77✔
495
        } else {
496
            this.treeService.collapse(this);
1,902✔
497
        }
498
    }
499

500
    /** @hidden @internal */
501
    public get expandIndicatorTemplate(): TemplateRef<any> {
502
        return this.tree?.expandIndicator || this._defaultExpandIndicatorTemplate;
39,921✔
503
    }
504

505
    /**
506
     * The native DOM element representing the node. Could be null in certain environments.
507
     *
508
     * ```typescript
509
     * // get the nativeElement of the second node
510
     * const node: IgxTreeNode = this.tree.nodes.first();
511
     * const nodeElement: HTMLElement = node.nativeElement;
512
     * ```
513
     */
514
    /** @hidden @internal */
515
    public get nativeElement() {
516
        return this.element.nativeElement;
1,953✔
517
    }
518

519
    /** @hidden @internal */
520
    public ngOnInit() {
521
        this.openAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(
3,843✔
522
            () => {
523
                this.tree.nodeExpanded.emit({ owner: this.tree, node: this });
12✔
524
            }
525
        );
526
        this.closeAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {
3,843✔
527
            this.tree.nodeCollapsed.emit({ owner: this.tree, node: this });
3✔
528
            this.treeService.collapse(this);
3✔
529
            this.cdr.markForCheck();
3✔
530
        });
531
    }
532

533
    /**
534
     * @hidden @internal
535
     * Sets the focus to the node's <a> child, if present
536
     * Sets the node as the tree service's focusedNode
537
     * Marks the node as the current active element
538
     */
539
    public handleFocus(): void {
540
        if (this.disabled) {
48!
541
            return;
×
542
        }
543
        if (this.navService.focusedNode !== this) {
48!
UNCOV
544
            this.navService.focusedNode = this;
×
545
        }
546
        this.isFocused = true;
48✔
547
        if (this.linkChildren?.length) {
48✔
548
            this.linkChildren.first.nativeElement.focus();
1✔
549
            return;
1✔
550
        }
551
        if (this.registeredChildren.length) {
47✔
552
            this.registeredChildren[0].elementRef.nativeElement.focus();
1✔
553
            return;
1✔
554
        }
555
    }
556

557
    /**
558
     * @hidden @internal
559
     * Clear the node's focused status
560
     */
561
    public clearFocus(): void {
562
        this.isFocused = false;
32✔
563
    }
564

565
    /**
566
     * @hidden @internal
567
     */
568
    public onSelectorPointerDown(event: PointerEvent) {
UNCOV
569
        event.preventDefault();
×
UNCOV
570
        event.stopPropagation()
×
571
    }
572

573
    /**
574
     * @hidden @internal
575
     */
576
    public onSelectorClick(event: MouseEvent) {
577
        // event.stopPropagation();
578
        event.preventDefault();
30✔
579
        // this.navService.handleFocusedAndActiveNode(this);
580
        if (event.shiftKey) {
30✔
581
            this.selectionService.selectMultipleNodes(this, event);
2✔
582
            return;
2✔
583
        }
584
        if (this.selected) {
28✔
585
            this.selectionService.deselectNode(this, event);
11✔
586
        } else {
587
            this.selectionService.selectNode(this, event);
17✔
588
        }
589
    }
590

591
    /**
592
     * Toggles the node expansion state, triggering animation
593
     *
594
     * ```html
595
     * <igx-tree>
596
     *      <igx-tree-node #node>My Node</igx-tree-node>
597
     * </igx-tree>
598
     * <button type="button" igxButton (click)="node.toggle()">Toggle Node</button>
599
     * ```
600
     *
601
     * ```typescript
602
     * const myNode: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
603
     * myNode.toggle();
604
     * ```
605
     */
606
    public toggle() {
607
        if (this.expanded) {
13✔
608
            this.collapse();
1✔
609
        } else {
610
            this.expand();
12✔
611
        }
612
    }
613

614
    /** @hidden @internal */
615
    public indicatorClick() {
616
        if(!this.tree.toggleNodeOnClick) {
10✔
617
            this.toggle();
10✔
618
            this.navService.setFocusedAndActiveNode(this);
10✔
619
        }
620
    }
621

622
    /**
623
     * @hidden @internal
624
     */
625
    public onPointerDown(event: PointerEvent) {
626
        event.stopPropagation();
25✔
627

628
        //Toggle the node only on left mouse click - https://w3c.github.io/pointerevents/#button-states
629
        if(this.tree.toggleNodeOnClick && event.button === 0) {
25✔
630
            this.toggle();
1✔
631
        }
632

633
        this.navService.setFocusedAndActiveNode(this);
25✔
634
    }
635

636
    public override ngOnDestroy() {
637
        super.ngOnDestroy();
3,855✔
638
        this.selectionService.ensureStateOnNodeDelete(this);
3,855✔
639
    }
640

641
    /**
642
     * Expands the node, triggering animation
643
     *
644
     * ```html
645
     * <igx-tree>
646
     *      <igx-tree-node #node>My Node</igx-tree-node>
647
     * </igx-tree>
648
     * <button type="button" igxButton (click)="node.expand()">Expand Node</button>
649
     * ```
650
     *
651
     * ```typescript
652
     * const myNode: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
653
     * myNode.expand();
654
     * ```
655
     */
656
    public expand() {
657
        if (this.expanded && !this.treeService.collapsingNodes.has(this)) {
27✔
658
            return;
1✔
659
        }
660
        const args: ITreeNodeTogglingEventArgs = {
26✔
661
            owner: this.tree,
662
            node: this,
663
            cancel: false
664

665
        };
666
        this.tree.nodeExpanding.emit(args);
26✔
667
        if (!args.cancel) {
26✔
668
            this.treeService.expand(this, true);
25✔
669
            this.cdr.detectChanges();
25✔
670
            this.playOpenAnimation(
25✔
671
                this.childrenContainer
672
            );
673
        }
674
    }
675

676
    /**
677
     * Collapses the node, triggering animation
678
     *
679
     * ```html
680
     * <igx-tree>
681
     *      <igx-tree-node #node>My Node</igx-tree-node>
682
     * </igx-tree>
683
     * <button type="button" igxButton (click)="node.collapse()">Collapse Node</button>
684
     * ```
685
     *
686
     * ```typescript
687
     * const myNode: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
688
     * myNode.collapse();
689
     * ```
690
     */
691
    public collapse() {
692
        if (!this.expanded || this.treeService.collapsingNodes.has(this)) {
6✔
693
            return;
2✔
694
        }
695
        const args: ITreeNodeTogglingEventArgs = {
4✔
696
            owner: this.tree,
697
            node: this,
698
            cancel: false
699

700
        };
701
        this.tree.nodeCollapsing.emit(args);
4✔
702
        if (!args.cancel) {
4✔
703
            this.treeService.collapsing(this);
3✔
704
            this.playCloseAnimation(
3✔
705
                this.childrenContainer
706
            );
707
        }
708
    }
709

710
    /** @hidden @internal */
711
    public addLinkChild(link: IgxTreeNodeLinkDirective) {
712
        this._tabIndex = -1;
15✔
713
        this.registeredChildren.push(link);
15✔
714
    }
715

716
    /** @hidden @internal */
717
    public removeLinkChild(link: IgxTreeNodeLinkDirective) {
718
        const index = this.registeredChildren.indexOf(link);
30✔
719
        if (index !== -1) {
30✔
720
            this.registeredChildren.splice(index, 1);
15✔
721
        }
722
    }
723
}
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