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

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

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

13.33
/projects/igniteui-angular/src/lib/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
    Inject,
11
    Input,
12
    OnDestroy,
13
    OnInit,
14
    Optional,
15
    Output,
16
    QueryList,
17
    SkipSelf,
18
    TemplateRef,
19
    ViewChild,
20
    booleanAttribute
21
} from '@angular/core';
22
import { takeUntil } from 'rxjs/operators';
23
import { ITreeResourceStrings, TreeResourceStringsEN } from '../../core/i18n/tree-resources';
24
import { ToggleAnimationPlayer, ToggleAnimationSettings } from '../../expansion-panel/toggle-animation-component';
25
import { IgxAngularAnimationService } from '../../services/animation/angular-animation-service';
26
import { AnimationService } from '../../services/animation/animation';
27
import {
28
    IgxTree,
29
    IgxTreeNode,
30
    IgxTreeSelectionType,
31
    IGX_TREE_COMPONENT,
32
    IGX_TREE_NODE_COMPONENT,
33
    ITreeNodeTogglingEventArgs
34
} from '../common';
35
import { IgxTreeNavigationService } from '../tree-navigation.service';
36
import { IgxTreeSelectionService } from '../tree-selection.service';
37
import { IgxTreeService } from '../tree.service';
38
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
39
import { IgxCheckboxComponent } from '../../checkbox/checkbox.component';
40
import { IgxIconComponent } from '../../icon/icon.component';
41
import { NgTemplateOutlet, NgIf, NgClass, NgFor } from '@angular/common';
42
import { getCurrentResourceStrings } from '../../core/i18n/resources';
43

44
// TODO: Implement aria functionality
45
/**
46
 * @hidden @internal
47
 * Used for links (`a` tags) in the body of an `igx-tree-node`. Handles aria and event dispatch.
48
 */
49
@Directive({
50
    selector: `[igxTreeNodeLink]`,
51
    standalone: true
52
})
53
export class IgxTreeNodeLinkDirective implements OnDestroy {
2✔
54

55
    @HostBinding('attr.role')
UNCOV
56
    public role = 'treeitem';
×
57

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

84
    public get parentNode(): any {
UNCOV
85
        return this._parentNode;
×
86
    }
87

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

UNCOV
93
    private _parentNode: IgxTreeNode<any> = null;
×
94

95
    constructor(
96
        @Optional() @Inject(IGX_TREE_NODE_COMPONENT)
UNCOV
97
        private node: IgxTreeNode<any>,
×
UNCOV
98
        private navService: IgxTreeNavigationService,
×
UNCOV
99
        public elementRef: ElementRef,
×
100
    ) { }
101

102
    /** @hidden @internal */
103
    @HostBinding('attr.tabindex')
104
    public get tabIndex(): number {
UNCOV
105
        return this.navService.focusedNode === this.target ? (this.target?.disabled ? -1 : 0) : -1;
×
106
    }
107

108
    /**
109
     * @hidden @internal
110
     * Clear the node's focused state
111
     */
112
    @HostListener('blur')
113
    public handleBlur() {
114
        this.target.isFocused = false;
×
115
    }
116

117
    /**
118
     * @hidden @internal
119
     * Set the node as focused
120
     */
121
    @HostListener('focus')
122
    public handleFocus() {
UNCOV
123
        if (this.target && !this.target.disabled) {
×
UNCOV
124
            if (this.navService.focusedNode !== this.target) {
×
125
                this.navService.focusedNode = this.target;
×
126
            }
UNCOV
127
            this.target.isFocused = true;
×
128
        }
129
    }
130

131
    public ngOnDestroy() {
UNCOV
132
        this.target.removeLinkChild(this);
×
133
    }
134
}
135

136
/**
137
 *
138
 * The tree node component represents a child node of the tree component or another tree node.
139
 * Usage:
140
 *
141
 * ```html
142
 *  <igx-tree>
143
 *  ...
144
 *    <igx-tree-node [data]="data" [selected]="service.isNodeSelected(data.Key)" [expanded]="service.isNodeExpanded(data.Key)">
145
 *      {{ data.FirstName }} {{ data.LastName }}
146
 *    </igx-tree-node>
147
 *  ...
148
 *  </igx-tree>
149
 * ```
150
 */
151
@Component({
152
    selector: 'igx-tree-node',
153
    templateUrl: 'tree-node.component.html',
154
    providers: [
155
        { provide: IGX_TREE_NODE_COMPONENT, useExisting: IgxTreeNodeComponent }
156
    ],
157
    imports: [NgTemplateOutlet, NgIf, IgxIconComponent, IgxCheckboxComponent, NgClass, NgFor, IgxCircularProgressBarComponent]
158
})
159
export class IgxTreeNodeComponent<T> extends ToggleAnimationPlayer implements IgxTreeNode<T>, OnInit, OnDestroy {
2✔
160
    /**
161
     * The data entry that the node is visualizing.
162
     *
163
     * @remarks
164
     * Required for searching through nodes.
165
     *
166
     * @example
167
     * ```html
168
     *  <igx-tree>
169
     *  ...
170
     *    <igx-tree-node [data]="data">
171
     *      {{ data.FirstName }} {{ data.LastName }}
172
     *    </igx-tree-node>
173
     *  ...
174
     *  </igx-tree>
175
     * ```
176
     */
177
    @Input()
178
    public data: T;
179

180
    /**
181
     * To be used for load-on-demand scenarios in order to specify whether the node is loading data.
182
     *
183
     * @remarks
184
     * Loading nodes do not render children.
185
     */
186
    @Input({ transform: booleanAttribute })
187
    public loading = false;
2✔
188

189
    // TO DO: return different tab index depending on anchor child
190
    /** @hidden @internal */
191
    public set tabIndex(val: number) {
UNCOV
192
        this._tabIndex = val;
×
193
    }
194

195
    /** @hidden @internal */
196
    public get tabIndex(): number {
UNCOV
197
        if (this.disabled) {
×
UNCOV
198
            return -1;
×
199
        }
UNCOV
200
        if (this._tabIndex === null) {
×
UNCOV
201
            if (this.navService.focusedNode === null) {
×
UNCOV
202
                return this.hasLinkChildren ? -1 : 0;
×
203
            }
UNCOV
204
            return -1;
×
205
        }
UNCOV
206
        return this.hasLinkChildren ? -1 : this._tabIndex;
×
207
    }
208

209
    /** @hidden @internal */
210
    public override get animationSettings(): ToggleAnimationSettings {
UNCOV
211
        return this.tree.animationSettings;
×
212
    }
213

214
    /**
215
     * Gets/Sets the resource strings.
216
     *
217
     * @remarks
218
     * Uses EN resources by default.
219
     */
220
    @Input()
221
    public set resourceStrings(value: ITreeResourceStrings) {
222
        this._resourceStrings = Object.assign({}, this._resourceStrings, value);
×
223
    }
224

225
    /**
226
     * An accessor that returns the resource strings.
227
     */
228
    public get resourceStrings(): ITreeResourceStrings {
UNCOV
229
        return this._resourceStrings;
×
230
    }
231

232
    /**
233
     * Gets/Sets the active state of the node
234
     *
235
     * @param value: boolean
236
     */
237
    @Input({ transform: booleanAttribute })
238
    public set active(value: boolean) {
UNCOV
239
        if (value) {
×
UNCOV
240
            this.navService.activeNode = this;
×
UNCOV
241
            this.tree.activeNodeBindingChange.emit(this);
×
242
        }
243
    }
244

245
    public get active(): boolean {
UNCOV
246
        return this.navService.activeNode === this;
×
247
    }
248

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

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

285
    /** @hidden @internal */
286
    public get focused() {
UNCOV
287
        return this.isFocused &&
×
288
            this.navService.focusedNode === this;
289
    }
290

291
    /**
292
     * Retrieves the full path to the node incuding itself
293
     *
294
     * ```typescript
295
     * const node: IgxTreeNode<any> = this.tree.findNodes(data[0])[0];
296
     * const path: IgxTreeNode<any>[] = node.path;
297
     * ```
298
     */
299
    public get path(): IgxTreeNode<any>[] {
UNCOV
300
        return this.parentNode?.path ? [...this.parentNode.path, this] : [this];
×
301
    }
302

303
    // TODO: bind to disabled state when node is dragged
304
    /**
305
     * Gets/Sets the disabled state of the node
306
     *
307
     * @param value: boolean
308
     */
309
    @Input({ transform: booleanAttribute })
310
    @HostBinding('class.igx-tree-node--disabled')
311
    public get disabled(): boolean {
UNCOV
312
        return this._disabled;
×
313
    }
314

315
    public set disabled(value: boolean) {
UNCOV
316
        if (value !== this._disabled) {
×
UNCOV
317
            this._disabled = value;
×
UNCOV
318
            this.tree.disabledChange.emit(this);
×
319
        }
320
    }
321

322
    /** @hidden @internal */
323
    @HostBinding('class.igx-tree-node')
324
    public cssClass = 'igx-tree-node';
2✔
325

326
    /** @hidden @internal */
327
    @HostBinding('attr.role')
328
    public get role() {
UNCOV
329
        return this.hasLinkChildren ? 'none' : 'treeitem';
×
330
    }
331

332
    /** @hidden @internal */
333
    @ContentChildren(IgxTreeNodeLinkDirective, { read: ElementRef })
334
    public linkChildren: QueryList<ElementRef>;
335

336
    /** @hidden @internal */
337
    @ContentChildren(IGX_TREE_NODE_COMPONENT, { read: IGX_TREE_NODE_COMPONENT })
338
    public _children: QueryList<IgxTreeNode<any>>;
339

340
    /** @hidden @internal */
341
    @ContentChildren(IGX_TREE_NODE_COMPONENT, { read: IGX_TREE_NODE_COMPONENT, descendants: true })
342
    public allChildren: QueryList<IgxTreeNode<any>>;
343

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

360
    // TODO: will be used in Drag and Drop implementation
361
    /** @hidden @internal */
362
    @ViewChild('ghostTemplate', { read: ElementRef })
363
    public header: ElementRef;
364

365
    @ViewChild('defaultIndicator', { read: TemplateRef, static: true })
366
    private _defaultExpandIndicatorTemplate: TemplateRef<any>;
367

368
    @ViewChild('childrenContainer', { read: ElementRef })
369
    private childrenContainer: ElementRef;
370

371
    private get hasLinkChildren(): boolean {
UNCOV
372
        return this.linkChildren?.length > 0 || this.registeredChildren?.length > 0;
×
373
    }
374

375
    /** @hidden @internal */
376
    public isFocused: boolean;
377

378
    /** @hidden @internal */
379
    public registeredChildren: IgxTreeNodeLinkDirective[] = [];
2✔
380

381
    /** @hidden @internal */
382
    private _resourceStrings = getCurrentResourceStrings(TreeResourceStringsEN);
2✔
383

384
    private _tabIndex = null;
2✔
385
    private _disabled = false;
2✔
386

387
    constructor(
388
        @Inject(IGX_TREE_COMPONENT) public tree: IgxTree,
2✔
389
        protected selectionService: IgxTreeSelectionService,
2✔
390
        protected treeService: IgxTreeService,
2✔
391
        protected navService: IgxTreeNavigationService,
2✔
392
        protected cdr: ChangeDetectorRef,
2✔
393
        @Inject(IgxAngularAnimationService) animationService: AnimationService,
394
        private element: ElementRef<HTMLElement>,
2✔
395
        @Optional() @SkipSelf() @Inject(IGX_TREE_NODE_COMPONENT) public parentNode: IgxTreeNode<any>
2✔
396
    ) {
397
        super(animationService);
2✔
398
    }
399

400
    /**
401
     * @hidden @internal
402
     */
403
    public get showSelectors() {
UNCOV
404
        return this.tree.selection !== IgxTreeSelectionType.None;
×
405
    }
406

407
    /**
408
     * @hidden @internal
409
     */
410
    public get indeterminate(): boolean {
UNCOV
411
        return this.selectionService.isNodeIndeterminate(this);
×
412
    }
413

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

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

456
    public set selected(val: boolean) {
UNCOV
457
        if (!(this.tree?.nodes && this.tree.nodes.find((e) => e === this)) && val) {
×
UNCOV
458
            this.tree.forceSelect.push(this);
×
UNCOV
459
            return;
×
460
        }
UNCOV
461
        if (val && !this.selectionService.isNodeSelected(this)) {
×
UNCOV
462
            this.selectionService.selectNodesWithNoEvent([this]);
×
463
        }
UNCOV
464
        if (!val && this.selectionService.isNodeSelected(this)) {
×
UNCOV
465
            this.selectionService.deselectNodesWithNoEvent([this]);
×
466
        }
467
    }
468

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

491
    public set expanded(val: boolean) {
UNCOV
492
        if (val) {
×
UNCOV
493
            this.treeService.expand(this, false);
×
494
        } else {
UNCOV
495
            this.treeService.collapse(this);
×
496
        }
497
    }
498

499
    /** @hidden @internal */
500
    public get expandIndicatorTemplate(): TemplateRef<any> {
UNCOV
501
        return this.tree?.expandIndicator || this._defaultExpandIndicatorTemplate;
×
502
    }
503

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

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

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

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

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

572
    /**
573
     * @hidden @internal
574
     */
575
    public onSelectorClick(event) {
576
        // event.stopPropagation();
UNCOV
577
        event.preventDefault();
×
578
        // this.navService.handleFocusedAndActiveNode(this);
UNCOV
579
        if (event.shiftKey) {
×
UNCOV
580
            this.selectionService.selectMultipleNodes(this, event);
×
UNCOV
581
            return;
×
582
        }
UNCOV
583
        if (this.selected) {
×
UNCOV
584
            this.selectionService.deselectNode(this, event);
×
585
        } else {
UNCOV
586
            this.selectionService.selectNode(this, event);
×
587
        }
588
    }
589

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

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

621
    /**
622
     * @hidden @internal
623
     */
624
    public onPointerDown(event) {
UNCOV
625
        event.stopPropagation();
×
626

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

UNCOV
632
        this.navService.setFocusedAndActiveNode(this);
×
633
    }
634

635
    public override ngOnDestroy() {
UNCOV
636
        super.ngOnDestroy();
×
UNCOV
637
        this.selectionService.ensureStateOnNodeDelete(this);
×
638
    }
639

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

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

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

699
        };
UNCOV
700
        this.tree.nodeCollapsing.emit(args);
×
UNCOV
701
        if (!args.cancel) {
×
UNCOV
702
            this.treeService.collapsing(this);
×
UNCOV
703
            this.playCloseAnimation(
×
704
                this.childrenContainer
705
            );
706
        }
707
    }
708

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

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

© 2025 Coveralls, Inc