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

IgniteUI / igniteui-angular / 16193550997

10 Jul 2025 11:12AM UTC coverage: 4.657% (-87.0%) from 91.64%
16193550997

Pull #16028

github

web-flow
Merge f7a9963b8 into 87246e3ce
Pull Request #16028: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15764 branches covered (1.13%)

18 of 19 new or added lines in 2 files covered. (94.74%)

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

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

0.75
/projects/igniteui-angular/src/lib/grids/tree-grid/tree-grid.component.ts
1
import {
2
    ChangeDetectionStrategy,
3
    Component,
4
    HostBinding,
5
    Input,
6
    OnInit,
7
    TemplateRef,
8
    ContentChild,
9
    AfterContentInit,
10
    ViewChild,
11
    DoCheck,
12
    AfterViewInit,
13
    ElementRef,
14
    NgZone,
15
    Inject,
16
    ChangeDetectorRef,
17
    IterableDiffers,
18
    ViewContainerRef,
19
    Optional,
20
    LOCALE_ID,
21
    Injector,
22
    EnvironmentInjector,
23
    CUSTOM_ELEMENTS_SCHEMA,
24
    booleanAttribute
25
} from '@angular/core';
26
import { DOCUMENT, NgClass, NgTemplateOutlet, NgStyle } from '@angular/common';
27

28
import { IgxTreeGridAPIService } from './tree-grid-api.service';
29
import { IgxGridBaseDirective } from '../grid-base.directive';
30
import { ITreeGridRecord } from './tree-grid.interfaces';
31
import { IRowDataCancelableEventArgs, IRowDataEventArgs, IRowToggleEventArgs } from '../common/events';
32
import {
33
    HierarchicalTransaction,
34
    HierarchicalState,
35
    TransactionType,
36
    TransactionEventOrigin,
37
    StateUpdateEvent
38
} from '../../services/transaction/transaction';
39
import { IgxFilteringService } from '../filtering/grid-filtering.service';
40
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
41
import { IgxGridSelectionService } from '../selection/selection.service';
42
import { mergeObjects, PlatformUtil } from '../../core/utils';
43
import { first, takeUntil } from 'rxjs/operators';
44
import { IgxRowLoadingIndicatorTemplateDirective } from './tree-grid.directives';
45
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
46
import { IgxGridNavigationService } from '../grid-navigation.service';
47
import { CellType, GridServiceType, GridType, IGX_GRID_BASE, IGX_GRID_SERVICE_BASE, RowType } from '../common/grid.interface';
48
import { IgxColumnComponent } from '../columns/column.component';
49
import { IgxTreeGridSelectionService } from './tree-grid-selection.service';
50
import { GridSelectionMode } from '../common/enums';
51
import { IgxSummaryRow, IgxTreeGridRow } from '../grid-public-row';
52
import { IgxGridCRUDService } from '../common/crud.service';
53
import { IgxTreeGridGroupByAreaComponent } from '../grouping/tree-grid-group-by-area.component';
54
import { IgxGridCell } from '../grid-public-cell';
55
import { IgxHierarchicalTransactionFactory } from '../../services/transaction/transaction-factory.service';
56
import { IgxColumnResizingService } from '../resizing/resizing.service';
57
import { HierarchicalTransactionService } from '../../services/transaction/hierarchical-transaction';
58
import { IgxOverlayService } from '../../services/overlay/overlay';
59
import { IgxGridTransaction } from '../common/types';
60
import { TreeGridFilteringStrategy } from './tree-grid.filtering.strategy';
61
import { IgxGridValidationService } from '../grid/grid-validation.service';
62
import { IgxTreeGridSummaryPipe } from './tree-grid.summary.pipe';
63
import { IgxTreeGridFilteringPipe } from './tree-grid.filtering.pipe';
64
import { IgxTreeGridHierarchizingPipe, IgxTreeGridFlatteningPipe, IgxTreeGridSortingPipe, IgxTreeGridPagingPipe, IgxTreeGridTransactionPipe, IgxTreeGridNormalizeRecordsPipe, IgxTreeGridAddRowPipe } from './tree-grid.pipes';
65
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
66
import { IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxStringReplacePipe } from '../common/pipes';
67
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
68
import { IgxIconComponent } from '../../icon/icon.component';
69
import { IgxRowEditTabStopDirective } from '../grid.rowEdit.directive';
70
import { IgxRippleDirective } from '../../directives/ripple/ripple.directive';
71
import { IgxButtonDirective } from '../../directives/button/button.directive';
72
import { IgxSnackbarComponent } from '../../snackbar/snackbar.component';
73
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
74
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive';
75
import { IgxSummaryRowComponent } from '../summaries/summary-row.component';
76
import { IgxTreeGridRowComponent } from './tree-grid-row.component';
77
import { IgxTemplateOutletDirective } from '../../directives/template-outlet/template_outlet.directive';
78
import { IgxGridForOfDirective } from '../../directives/for-of/for_of.directive';
79
import { IgxColumnMovingDropDirective } from '../moving/moving.drop.directive';
80
import { IgxGridDragSelectDirective } from '../selection/drag-select.directive';
81
import { IgxGridBodyDirective } from '../grid.common';
82
import { IgxGridHeaderRowComponent } from '../headers/grid-header-row.component';
83
import { IgxTextHighlightService } from '../../directives/text-highlight/text-highlight.service';
84

85
let NEXT_ID = 0;
3✔
86

87
/* blazorAdditionalDependency: Column */
88
/* blazorAdditionalDependency: ColumnGroup */
89
/* blazorAdditionalDependency: ColumnLayout */
90
/* blazorAdditionalDependency: GridToolbar */
91
/* blazorAdditionalDependency: GridToolbarActions */
92
/* blazorAdditionalDependency: GridToolbarTitle */
93
/* blazorAdditionalDependency: GridToolbarAdvancedFiltering */
94
/* blazorAdditionalDependency: GridToolbarExporter */
95
/* blazorAdditionalDependency: GridToolbarHiding */
96
/* blazorAdditionalDependency: GridToolbarPinning */
97
/* blazorAdditionalDependency: ActionStrip */
98
/* blazorAdditionalDependency: GridActionsBaseDirective */
99
/* blazorAdditionalDependency: GridEditingActions */
100
/* blazorAdditionalDependency: GridPinningActions */
101
/* blazorIndirectRender */
102
/**
103
 * **Ignite UI for Angular Tree Grid** -
104
 * [Documentation](https://www.infragistics.com/products/ignite-ui-angular/angular/components/grid/grid)
105
 *
106
 * The Ignite UI Tree Grid displays and manipulates hierarchical data with consistent schema formatted as a table and
107
 * provides features such as sorting, filtering, editing, column pinning, paging, column moving and hiding.
108
 *
109
 * Example:
110
 * ```html
111
 * <igx-tree-grid [data]="employeeData" primaryKey="employeeID" foreignKey="PID" [autoGenerate]="false">
112
 *   <igx-column field="first" header="First Name"></igx-column>
113
 *   <igx-column field="last" header="Last Name"></igx-column>
114
 *   <igx-column field="role" header="Role"></igx-column>
115
 * </igx-tree-grid>
116
 * ```
117
 */
118
@Component({
119
    changeDetection: ChangeDetectionStrategy.OnPush,
120
    selector: 'igx-tree-grid',
121
    templateUrl: 'tree-grid.component.html',
122
    providers: [
123
        IgxGridCRUDService,
124
        IgxGridValidationService,
125
        IgxGridSummaryService,
126
        IgxGridNavigationService,
127
        { provide: IgxGridSelectionService, useClass: IgxTreeGridSelectionService },
128
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxTreeGridAPIService },
129
        { provide: IGX_GRID_BASE, useExisting: IgxTreeGridComponent },
130
        IgxFilteringService,
131
        IgxColumnResizingService,
132
        IgxForOfSyncService,
133
        IgxForOfScrollSyncService
134
    ],
135
    imports: [
136
        NgClass,
137
        NgStyle,
138
        NgTemplateOutlet,
139
        IgxGridHeaderRowComponent,
140
        IgxGridBodyDirective,
141
        IgxGridDragSelectDirective,
142
        IgxColumnMovingDropDirective,
143
        IgxGridForOfDirective,
144
        IgxTemplateOutletDirective,
145
        IgxTreeGridRowComponent,
146
        IgxSummaryRowComponent,
147
        IgxOverlayOutletDirective,
148
        IgxToggleDirective,
149
        IgxCircularProgressBarComponent,
150
        IgxSnackbarComponent,
151
        IgxButtonDirective,
152
        IgxRippleDirective,
153
        IgxRowEditTabStopDirective,
154
        IgxIconComponent,
155
        IgxGridColumnResizerComponent,
156
        IgxHasVisibleColumnsPipe,
157
        IgxGridRowPinningPipe,
158
        IgxGridRowClassesPipe,
159
        IgxGridRowStylesPipe,
160
        IgxSummaryDataPipe,
161
        IgxTreeGridHierarchizingPipe,
162
        IgxTreeGridFlatteningPipe,
163
        IgxTreeGridSortingPipe,
164
        IgxTreeGridFilteringPipe,
165
        IgxTreeGridPagingPipe,
166
        IgxTreeGridTransactionPipe,
167
        IgxTreeGridSummaryPipe,
168
        IgxTreeGridNormalizeRecordsPipe,
169
        IgxTreeGridAddRowPipe,
170
        IgxStringReplacePipe
171
    ],
172
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
173
})
174
export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridType, OnInit, AfterViewInit, DoCheck, AfterContentInit {
3✔
175
    /**
176
     * Sets the child data key of the `IgxTreeGridComponent`.
177
     * ```html
178
     * <igx-tree-grid #grid [data]="employeeData" [childDataKey]="'employees'" [autoGenerate]="true"></igx-tree-grid>
179
     * ```
180
     *
181
     * @memberof IgxTreeGridComponent
182
     */
183
    @Input()
184
    public childDataKey: string;
185

186
    /**
187
     * Sets the foreign key of the `IgxTreeGridComponent`.
188
     * ```html
189
     * <igx-tree-grid #grid [data]="employeeData" [primaryKey]="'employeeID'" [foreignKey]="'parentID'" [autoGenerate]="true">
190
     * </igx-tree-grid>
191
     * ```
192
     *
193
     * @memberof IgxTreeGridComponent
194
     */
195
    @Input()
196
    public foreignKey: string;
197

198
    /**
199
     * Sets the key indicating whether a row has children.
200
     * This property is only used for load on demand scenarios.
201
     * ```html
202
     * <igx-tree-grid #grid [data]="employeeData" [primaryKey]="'employeeID'" [foreignKey]="'parentID'"
203
     *                [loadChildrenOnDemand]="loadChildren"
204
     *                [hasChildrenKey]="'hasEmployees'">
205
     * </igx-tree-grid>
206
     * ```
207
     *
208
     * @memberof IgxTreeGridComponent
209
     */
210
    @Input()
211
    public hasChildrenKey: string;
212

213
    /**
214
     * Sets whether child records should be deleted when their parent gets deleted.
215
     * By default it is set to true and deletes all children along with the parent.
216
     * ```html
217
     * <igx-tree-grid [data]="employeeData" [primaryKey]="'employeeID'" [foreignKey]="'parentID'" cascadeOnDelete="false">
218
     * </igx-tree-grid>
219
     * ```
220
     *
221
     * @memberof IgxTreeGridComponent
222
     */
223
    @Input({ transform: booleanAttribute })
UNCOV
224
    public cascadeOnDelete = true;
×
225

226
    /* csSuppress */
227
    /**
228
     * Sets a callback for loading child rows on demand.
229
     * ```html
230
     * <igx-tree-grid [data]="employeeData" [primaryKey]="'employeeID'" [foreignKey]="'parentID'" [loadChildrenOnDemand]="loadChildren">
231
     * </igx-tree-grid>
232
     * ```
233
     * ```typescript
234
     * public loadChildren = (parentID: any, done: (children: any[]) => void) => {
235
     *     this.dataService.getData(parentID, children => done(children));
236
     * }
237
     * ```
238
     *
239
     * @memberof IgxTreeGridComponent
240
     */
241
    @Input()
242
    public loadChildrenOnDemand: (parentID: any, done: (children: any[]) => void) => void;
243

244
    /**
245
     * @hidden @internal
246
     */
247
    @HostBinding('attr.role')
UNCOV
248
    public role = 'treegrid';
×
249

250
    /**
251
     * Sets the value of the `id` attribute. If not provided it will be automatically generated.
252
     * ```html
253
     * <igx-tree-grid [id]="'igx-tree-grid-1'"></igx-tree-grid>
254
     * ```
255
     *
256
     * @memberof IgxTreeGridComponent
257
     */
258
    @HostBinding('attr.id')
259
    @Input()
UNCOV
260
    public id = `igx-tree-grid-${NEXT_ID++}`;
×
261

262
    /**
263
     * @hidden
264
     * @internal
265
     */
266
    @ContentChild(IgxTreeGridGroupByAreaComponent, { read: IgxTreeGridGroupByAreaComponent })
267
    public treeGroupArea: IgxTreeGridGroupByAreaComponent;
268

269
    /**
270
     * @hidden @internal
271
     */
272
    @ViewChild('record_template', { read: TemplateRef, static: true })
273
    protected recordTemplate: TemplateRef<any>;
274

275
    /**
276
     * @hidden @internal
277
     */
278
    @ViewChild('summary_template', { read: TemplateRef, static: true })
279
    protected summaryTemplate: TemplateRef<any>;
280

281
    /**
282
     * @hidden
283
     */
284
    @ContentChild(IgxRowLoadingIndicatorTemplateDirective, { read: IgxRowLoadingIndicatorTemplateDirective })
285
    protected rowLoadingTemplate: IgxRowLoadingIndicatorTemplateDirective;
286

287
    /**
288
     * @hidden
289
     */
290
    public flatData: any[] | null;
291

292
    /**
293
     * @hidden
294
     */
295
    public processedExpandedFlatData: any[] | null;
296

297
    /**
298
     * Returns an array of the root level `ITreeGridRecord`s.
299
     * ```typescript
300
     * // gets the root record with index=2
301
     * const states = this.grid.rootRecords[2];
302
     * ```
303
     *
304
     * @memberof IgxTreeGridComponent
305
     */
306
    public rootRecords: ITreeGridRecord[];
307

308
    /* blazorSuppress */
309
    /**
310
     * Returns a map of all `ITreeGridRecord`s.
311
     * ```typescript
312
     * // gets the record with primaryKey=2
313
     * const states = this.grid.records.get(2);
314
     * ```
315
     *
316
     * @memberof IgxTreeGridComponent
317
     */
UNCOV
318
    public records: Map<any, ITreeGridRecord> = new Map<any, ITreeGridRecord>();
×
319

320
    /**
321
     * Returns an array of processed (filtered and sorted) root `ITreeGridRecord`s.
322
     * ```typescript
323
     * // gets the processed root record with index=2
324
     * const states = this.grid.processedRootRecords[2];
325
     * ```
326
     *
327
     * @memberof IgxTreeGridComponent
328
     */
329
    public processedRootRecords: ITreeGridRecord[];
330

331
    /* blazorSuppress */
332
    /**
333
     * Returns a map of all processed (filtered and sorted) `ITreeGridRecord`s.
334
     * ```typescript
335
     * // gets the processed record with primaryKey=2
336
     * const states = this.grid.processedRecords.get(2);
337
     * ```
338
     *
339
     * @memberof IgxTreeGridComponent
340
     */
UNCOV
341
    public processedRecords: Map<any, ITreeGridRecord> = new Map<any, ITreeGridRecord>();
×
342

343
    /**
344
     * @hidden
345
     */
UNCOV
346
    public loadingRows = new Set<any>();
×
347

UNCOV
348
    protected override _filterStrategy = new TreeGridFilteringStrategy();
×
349
    protected override _transactions: HierarchicalTransactionService<HierarchicalTransaction, HierarchicalState>;
350
    private _data;
351
    private _rowLoadingIndicatorTemplate: TemplateRef<void>;
UNCOV
352
    private _expansionDepth = Infinity;
×
353

354
     /* treatAsRef */
355
    /**
356
     * Gets/Sets the array of data that populates the component.
357
     * ```html
358
     * <igx-tree-grid [data]="Data" [autoGenerate]="true"></igx-tree-grid>
359
     * ```
360
     *
361
     * @memberof IgxTreeGridComponent
362
     */
363
    @Input()
364
    public get data(): any[] | null {
UNCOV
365
        return this._data;
×
366
    }
367

368
     /* treatAsRef */
369
    public set data(value: any[] | null) {
UNCOV
370
        const oldData = this._data;
×
UNCOV
371
        this._data = value || [];
×
UNCOV
372
        this.summaryService.clearSummaryCache();
×
UNCOV
373
        if (!this._init) {
×
UNCOV
374
            this.validation.updateAll(this._data);
×
375
        }
UNCOV
376
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
×
UNCOV
377
            this.setupColumns();
×
378
        }
UNCOV
379
        this.checkPrimaryKeyField();
×
UNCOV
380
        this.cdr.markForCheck();
×
381
    }
382

383
    /** @hidden @internal */
384
    public override get type(): GridType["type"] {
UNCOV
385
        return 'tree';
×
386
    }
387

388
    /**
389
     * Get transactions service for the grid.
390
     *
391
     * @experimental @hidden
392
     */
393
    public override get transactions() {
UNCOV
394
        if (this._diTransactions && !this.batchEditing) {
×
UNCOV
395
            return this._diTransactions;
×
396
        }
UNCOV
397
        return this._transactions;
×
398
    }
399

400
    /**
401
     * Sets the count of levels to be expanded in the `IgxTreeGridComponent`. By default it is
402
     * set to `Infinity` which means all levels would be expanded.
403
     * ```html
404
     * <igx-tree-grid #grid [data]="employeeData" [childDataKey]="'employees'" expansionDepth="1" [autoGenerate]="true"></igx-tree-grid>
405
     * ```
406
     *
407
     * @memberof IgxTreeGridComponent
408
     */
409
    @Input()
410
    public get expansionDepth(): number {
UNCOV
411
        return this._expansionDepth;
×
412
    }
413

414
    public set expansionDepth(value: number) {
UNCOV
415
        this._expansionDepth = value;
×
UNCOV
416
        this.notifyChanges();
×
417
    }
418

419
    /**
420
     * Template for the row loading indicator when load on demand is enabled.
421
     * ```html
422
     * <ng-template #rowLoadingTemplate>
423
     *     <igx-icon>loop</igx-icon>
424
     * </ng-template>
425
     *
426
     * <igx-tree-grid #grid [data]="employeeData" [primaryKey]="'ID'" [foreignKey]="'parentID'"
427
     *                [loadChildrenOnDemand]="loadChildren"
428
     *                [rowLoadingIndicatorTemplate]="rowLoadingTemplate">
429
     * </igx-tree-grid>
430
     * ```
431
     *
432
     * @memberof IgxTreeGridComponent
433
     */
434
    @Input()
435
    public get rowLoadingIndicatorTemplate(): TemplateRef<void> {
UNCOV
436
        return this._rowLoadingIndicatorTemplate;
×
437
    }
438

439
    public set rowLoadingIndicatorTemplate(value: TemplateRef<void>) {
440
        this._rowLoadingIndicatorTemplate = value;
×
441
        this.notifyChanges();
×
442
    }
443

444
    // Kind of stupid
445
    // private get _gridAPI(): IgxTreeGridAPIService {
446
    //     return this.gridAPI as IgxTreeGridAPIService;
447
    // }
448

449
    constructor(
450
        validationService: IgxGridValidationService,
451
        selectionService: IgxGridSelectionService,
452
        colResizingService: IgxColumnResizingService,
453
        @Inject(IGX_GRID_SERVICE_BASE) gridAPI: GridServiceType,
454
        // public gridAPI: GridBaseAPIService<IgxGridBaseDirective & GridType>,
455
        transactionFactory: IgxHierarchicalTransactionFactory,
456
        _elementRef: ElementRef<HTMLElement>,
457
        _zone: NgZone,
458
        @Inject(DOCUMENT) document: any,
459
        cdr: ChangeDetectorRef,
460
        differs: IterableDiffers,
461
        viewRef: ViewContainerRef,
462
        injector: Injector,
463
        envInjector: EnvironmentInjector,
464
        navigation: IgxGridNavigationService,
465
        filteringService: IgxFilteringService,
466
        textHighlightService: IgxTextHighlightService,
467
        @Inject(IgxOverlayService) overlayService: IgxOverlayService,
468
        summaryService: IgxGridSummaryService,
469
        @Inject(LOCALE_ID) localeId: string,
470
        platform: PlatformUtil,
UNCOV
471
        @Optional() @Inject(IgxGridTransaction) protected override _diTransactions?:
×
472
            HierarchicalTransactionService<HierarchicalTransaction, HierarchicalState>,
473
    ) {
UNCOV
474
        super(
×
475
            validationService,
476
            selectionService,
477
            colResizingService,
478
            gridAPI,
479
            transactionFactory,
480
            _elementRef,
481
            _zone,
482
            document,
483
            cdr,
484
            differs,
485
            viewRef,
486
            injector,
487
            envInjector,
488
            navigation,
489
            filteringService,
490
            textHighlightService,
491
            overlayService,
492
            summaryService,
493
            localeId,
494
            platform,
495
            _diTransactions,
496
        );
497
    }
498

499
    /**
500
     * @hidden
501
     */
502
    public override ngOnInit() {
UNCOV
503
        super.ngOnInit();
×
504

UNCOV
505
        this.rowToggle.pipe(takeUntil(this.destroy$)).subscribe((args) => {
×
UNCOV
506
            this.loadChildrenOnRowExpansion(args);
×
507
        });
508

509
        // TODO: cascade selection logic should be refactor to be handled in the already existing subs
UNCOV
510
        this.rowAddedNotifier.pipe(takeUntil(this.destroy$)).subscribe(args => {
×
UNCOV
511
            if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
512
                let rec = this.gridAPI.get_rec_by_id(this.primaryKey ? args.data[this.primaryKey] : args.data);
×
UNCOV
513
                if (rec && rec.parent) {
×
UNCOV
514
                    this.gridAPI.grid.selectionService.updateCascadeSelectionOnFilterAndCRUD(
×
515
                        new Set([rec.parent]), rec.parent.key);
516
                } else {
517
                    // The record is still not available
518
                    // Wait for the change detection to update records through pipes
UNCOV
519
                    requestAnimationFrame(() => {
×
UNCOV
520
                        rec = this.gridAPI.get_rec_by_id(this.primaryKey ?
×
521
                            args.data[this.primaryKey] : args.data);
UNCOV
522
                        if (rec && rec.parent) {
×
UNCOV
523
                            this.gridAPI.grid.selectionService.updateCascadeSelectionOnFilterAndCRUD(
×
524
                                new Set([rec.parent]), rec.parent.key);
525
                        }
UNCOV
526
                        this.notifyChanges();
×
527
                    });
528
                }
529
            }
530
        });
531

UNCOV
532
        this.rowDeletedNotifier.pipe(takeUntil(this.destroy$)).subscribe(args => {
×
UNCOV
533
            if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
534
                if (args.data) {
×
UNCOV
535
                    const rec = this.gridAPI.get_rec_by_id(
×
536
                        this.primaryKey ? args.data[this.primaryKey] : args.data);
×
UNCOV
537
                    this.handleCascadeSelection(args, rec);
×
538
                } else {
539
                    // if a row has been added and before commiting the transaction deleted
540
                    const leafRowsDirectParents = new Set<any>();
×
541
                    this.records.forEach(record => {
×
542
                        if (record && (!record.children || record.children.length === 0) && record.parent) {
×
543
                            leafRowsDirectParents.add(record.parent);
×
544
                        }
545
                    });
546
                    // Wait for the change detection to update records through pipes
547
                    requestAnimationFrame(() => {
×
548
                        this.gridAPI.grid.selectionService.updateCascadeSelectionOnFilterAndCRUD(leafRowsDirectParents);
×
549
                        this.notifyChanges();
×
550
                    });
551
                }
552
            }
553
        });
554

UNCOV
555
        this.filteringDone.pipe(takeUntil(this.destroy$)).subscribe(() => {
×
UNCOV
556
            if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
557
                const leafRowsDirectParents = new Set<any>();
×
UNCOV
558
                this.records.forEach(record => {
×
UNCOV
559
                    if (record && (!record.children || record.children.length === 0) && record.parent) {
×
UNCOV
560
                        leafRowsDirectParents.add(record.parent);
×
561
                    }
562
                });
UNCOV
563
                this.gridAPI.grid.selectionService.updateCascadeSelectionOnFilterAndCRUD(leafRowsDirectParents);
×
UNCOV
564
                this.notifyChanges();
×
565
            }
566
        });
567
    }
568

569
    /**
570
     * @hidden
571
     */
572
    public override ngAfterViewInit() {
UNCOV
573
        super.ngAfterViewInit();
×
574
        // TODO: pipesExectured event
575
        // run after change detection in super triggers pipes for records structure
UNCOV
576
        if (this.rowSelection === GridSelectionMode.multipleCascade && this.selectedRows.length) {
×
577
            const selRows = this.selectedRows;
×
578
            this.selectionService.clearRowSelection();
×
579
            this.selectRows(selRows, true);
×
580
            this.cdr.detectChanges();
×
581
        }
582
    }
583

584
    /**
585
     * @hidden
586
     */
587
    public override ngAfterContentInit() {
UNCOV
588
        if (this.rowLoadingTemplate) {
×
589
            this._rowLoadingIndicatorTemplate = this.rowLoadingTemplate.template;
×
590
        }
UNCOV
591
        super.ngAfterContentInit();
×
592
    }
593

594
    public override getDefaultExpandState(record: ITreeGridRecord) {
595
        return record.children && record.children.length && record.level < this.expansionDepth;
×
596
    }
597

598
    /**
599
     * Expands all rows.
600
     * ```typescript
601
     * this.grid.expandAll();
602
     * ```
603
     *
604
     * @memberof IgxTreeGridComponent
605
     */
606
    public override expandAll() {
UNCOV
607
        this._expansionDepth = Infinity;
×
UNCOV
608
        this.expansionStates = new Map<any, boolean>();
×
609
    }
610

611
    /**
612
     * Collapses all rows.
613
     *
614
     * ```typescript
615
     * this.grid.collapseAll();
616
     *  ```
617
     *
618
     * @memberof IgxTreeGridComponent
619
     */
620
    public override collapseAll() {
UNCOV
621
        this._expansionDepth = 0;
×
UNCOV
622
        this.expansionStates = new Map<any, boolean>();
×
623
    }
624

625
    /**
626
     * @hidden
627
     */
628
    public override refreshGridState(args?: IRowDataEventArgs) {
UNCOV
629
        super.refreshGridState();
×
UNCOV
630
        if (this.primaryKey && this.foreignKey && args) {
×
UNCOV
631
            const rowID = args.data[this.foreignKey];
×
UNCOV
632
            this.summaryService.clearSummaryCache({ rowID });
×
UNCOV
633
            this.pipeTrigger++;
×
UNCOV
634
            this.cdr.detectChanges();
×
635
        }
636
    }
637

638
    /* blazorCSSuppress */
639
    /**
640
     * Creates a new `IgxTreeGridRowComponent` with the given data. If a parentRowID is not specified, the newly created
641
     * row would be added at the root level. Otherwise, it would be added as a child of the row whose primaryKey matches
642
     * the specified parentRowID. If the parentRowID does not exist, an error would be thrown.
643
     * ```typescript
644
     * const record = {
645
     *     ID: this.grid.data[this.grid1.data.length - 1].ID + 1,
646
     *     Name: this.newRecord
647
     * };
648
     * this.grid.addRow(record, 1); // Adds a new child row to the row with ID=1.
649
     * ```
650
     *
651
     * @param data
652
     * @param parentRowID
653
     * @memberof IgxTreeGridComponent
654
     */
655
    // TODO: remove evt emission
656
    public override addRow(data: any, parentRowID?: any) {
UNCOV
657
        this.crudService.endEdit(true);
×
UNCOV
658
        this.gridAPI.addRowToData(data, parentRowID);
×
659

UNCOV
660
        this.rowAddedNotifier.next({
×
661
            data: data,
662
            rowData: data, owner: this,
663
            primaryKey: data[this.primaryKey],
664
            rowKey: data[this.primaryKey]
665
        });
UNCOV
666
        this.pipeTrigger++;
×
UNCOV
667
        this.notifyChanges();
×
668
    }
669

670
    /**
671
     * Enters add mode by spawning the UI with the context of the specified row by index.
672
     *
673
     * @remarks
674
     * Accepted values for index are integers from 0 to this.grid.dataView.length
675
     * @remarks
676
     * When adding the row as a child, the parent row is the specified row.
677
     * @remarks
678
     * To spawn the UI on top, call the function with index = null or a negative number.
679
     * In this case trying to add this row as a child will result in error.
680
     * @example
681
     * ```typescript
682
     * this.grid.beginAddRowByIndex(10);
683
     * this.grid.beginAddRowByIndex(10, true);
684
     * this.grid.beginAddRowByIndex(null);
685
     * ```
686
     * @param index - The index to spawn the UI at. Accepts integers from 0 to this.grid.dataView.length
687
     * @param asChild - Whether the record should be added as a child. Only applicable to igxTreeGrid.
688
     */
689
    public override beginAddRowByIndex(index: number, asChild?: boolean): void {
690
        if (index === null || index < 0) {
×
691
            return this.beginAddRowById(null, asChild);
×
692
        }
693
        return this._addRowForIndex(index - 1, asChild);
×
694
    }
695

696
    /**
697
     * @hidden
698
     */
699
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
UNCOV
700
        return {
×
701
            $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
×
702
            index: this.getDataViewIndex(rowIndex, pinned),
703
            templateID: {
704
                type: this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
×
705
                id: null
706
            },
707
            disabled: this.isGhostRecord(rowData) ? rowData.recordRef.isFilteredOutParent === undefined : false
×
708
        };
709
    }
710

711
    /**
712
     * @hidden
713
     * @internal
714
     */
715
    public override getInitialPinnedIndex(rec) {
UNCOV
716
        const id = this.gridAPI.get_row_id(rec);
×
UNCOV
717
        return this._pinnedRecordIDs.indexOf(id);
×
718
    }
719

720
    /**
721
     * @hidden
722
     * @internal
723
     */
724
    public override isRecordPinned(rec) {
UNCOV
725
        return this.getInitialPinnedIndex(rec.data) !== -1;
×
726
    }
727

728
    /**
729
     *
730
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
731
     *
732
     * @remarks
733
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
734
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
735
     */
736
    public override getSelectedData(formatters = false, headers = false): any[] {
×
UNCOV
737
        let source = [];
×
738

UNCOV
739
        const process = (record) => {
×
UNCOV
740
            if (record.summaries) {
×
UNCOV
741
                source.push(null);
×
UNCOV
742
                return;
×
743
            }
UNCOV
744
            source.push(record.data);
×
745
        };
746

UNCOV
747
        this.unpinnedDataView.forEach(process);
×
UNCOV
748
        source = this.isRowPinningToTop ? [...this.pinnedDataView, ...source] : [...source, ...this.pinnedDataView];
×
UNCOV
749
        return this.extractDataFromSelection(source, formatters, headers);
×
750
    }
751

752
    /**
753
     * @hidden @internal
754
     */
755
    public override getEmptyRecordObjectFor(inTreeRow: RowType) {
UNCOV
756
        const treeRowRec = inTreeRow?.treeRow || null;
×
UNCOV
757
        const row = { ...treeRowRec };
×
UNCOV
758
        const data = treeRowRec?.data || {};
×
UNCOV
759
        row.data = { ...data };
×
UNCOV
760
        Object.keys(row.data).forEach(key => {
×
761
            // persist foreign key if one is set.
UNCOV
762
            if (this.foreignKey && key === this.foreignKey) {
×
UNCOV
763
                row.data[key] = treeRowRec.data[this.crudService.addRowParent?.asChild ? this.primaryKey : key];
×
764
            } else {
UNCOV
765
                row.data[key] = undefined;
×
766
            }
767
        });
UNCOV
768
        let id = this.generateRowID();
×
UNCOV
769
        const rootRecPK = this.foreignKey && this.rootRecords && this.rootRecords.length > 0 ?
×
770
            this.rootRecords[0].data[this.foreignKey] : null;
UNCOV
771
        if (id === rootRecPK) {
×
772
            // safeguard in case generated id matches the root foreign key.
UNCOV
773
            id = this.generateRowID();
×
774
        }
UNCOV
775
        row.key = id;
×
UNCOV
776
        row.data[this.primaryKey] = id;
×
UNCOV
777
        return { rowID: id, data: row.data, recordRef: row };
×
778
    }
779

780
    /** @hidden */
781
    public override deleteRowById(rowId: any): any {
782
        //  if this is flat self-referencing data, and CascadeOnDelete is set to true
783
        //  and if we have transactions we should start pending transaction. This allows
784
        //  us in case of delete action to delete all child rows as single undo action
UNCOV
785
        const args: IRowDataCancelableEventArgs = {
×
786
            rowID: rowId,
787
            primaryKey: rowId,
788
            rowKey: rowId,
789
            cancel: false,
790
            rowData: this.getRowData(rowId),
791
            data: this.getRowData(rowId),
792
            oldValue: null,
793
            owner: this
794
        };
UNCOV
795
        this.rowDelete.emit(args);
×
UNCOV
796
        if (args.cancel) {
×
UNCOV
797
            return;
×
798
        }
799

UNCOV
800
        const record = this.gridAPI.deleteRowById(rowId);
×
UNCOV
801
        const key = record[this.primaryKey];
×
UNCOV
802
        if (record !== null && record !== undefined) {
×
UNCOV
803
            const rowDeletedEventArgs: IRowDataEventArgs = {
×
804
                data: record,
805
                rowData: record,
806
                owner: this,
807
                primaryKey: key,
808
                rowKey: key
809
            };
UNCOV
810
            this.rowDeleted.emit(rowDeletedEventArgs);
×
811
        }
UNCOV
812
        return record;
×
813
    }
814

815
    /**
816
     * Returns the `IgxTreeGridRow` by index.
817
     *
818
     * @example
819
     * ```typescript
820
     * const myRow = treeGrid.getRowByIndex(1);
821
     * ```
822
     * @param index
823
     */
824
    public getRowByIndex(index: number): RowType {
UNCOV
825
        if (index < 0 || index >= this.dataView.length) {
×
UNCOV
826
            return undefined;
×
827
        }
UNCOV
828
        return this.createRow(index);
×
829
    }
830

831
    /**
832
     * Returns the `RowType` object by the specified primary key.
833
     *
834
     * @example
835
     * ```typescript
836
     * const myRow = this.treeGrid.getRowByIndex(1);
837
     * ```
838
     * @param index
839
     */
840
    public getRowByKey(key: any): RowType {
UNCOV
841
        const rec = this.filteredSortedData ? this.primaryKey ? this.filteredSortedData.find(r => r[this.primaryKey] === key) :
×
UNCOV
842
            this.filteredSortedData.find(r => r === key) : undefined;
×
UNCOV
843
        const index = this.dataView.findIndex(r => r.data && r.data === rec);
×
UNCOV
844
        if (index < 0 || index >= this.filteredSortedData.length) {
×
UNCOV
845
            return undefined;
×
846
        }
UNCOV
847
        return new IgxTreeGridRow(this as any, index, rec);
×
848
    }
849

850
    /**
851
     * Returns the collection of all RowType for current page.
852
     *
853
     * @hidden @internal
854
     */
855
    public allRows(): RowType[] {
UNCOV
856
        return this.dataView.map((rec, index) => this.createRow(index));
×
857
    }
858

859
    /**
860
     * Returns the collection of `IgxTreeGridRow`s for current page.
861
     *
862
     * @hidden @internal
863
     */
864
    public dataRows(): RowType[] {
UNCOV
865
        return this.allRows().filter(row => row instanceof IgxTreeGridRow);
×
866
    }
867

868
    /**
869
     * Returns an array of the selected `IgxGridCell`s.
870
     *
871
     * @example
872
     * ```typescript
873
     * const selectedCells = this.grid.selectedCells;
874
     * ```
875
     */
876
    public get selectedCells(): CellType[] {
UNCOV
877
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
×
UNCOV
878
            .reduce((a, b) => a.concat(b), []);
×
879
    }
880

881
    /**
882
     * Returns a `CellType` object that matches the conditions.
883
     *
884
     * @example
885
     * ```typescript
886
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
887
     * ```
888
     * @param rowIndex
889
     * @param columnField
890
     */
891
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
UNCOV
892
        const row = this.getRowByIndex(rowIndex);
×
UNCOV
893
        const column = this.columns.find((col) => col.field === columnField);
×
UNCOV
894
        if (row && row instanceof IgxTreeGridRow && column) {
×
UNCOV
895
            return new IgxGridCell(this as any, rowIndex, column);
×
896
        }
897
    }
898

899
    /**
900
     * Returns a `CellType` object that matches the conditions.
901
     *
902
     * @remarks
903
     * Requires that the primaryKey property is set.
904
     * @example
905
     * ```typescript
906
     * grid.getCellByKey(1, 'index');
907
     * ```
908
     * @param rowSelector match any rowID
909
     * @param columnField
910
     */
911
    public getCellByKey(rowSelector: any, columnField: string): CellType {
UNCOV
912
        const row = this.getRowByKey(rowSelector);
×
UNCOV
913
        const column = this.columns.find((col) => col.field === columnField);
×
UNCOV
914
        if (row && column) {
×
UNCOV
915
            return new IgxGridCell(this as any, row.index, column);
×
916
        }
917
    }
918

919
    public override pinRow(rowID: any, index?: number): boolean {
UNCOV
920
        const row = this.getRowByKey(rowID);
×
UNCOV
921
        return super.pinRow(rowID, index, row);
×
922
    }
923

924
    public override unpinRow(rowID: any): boolean {
UNCOV
925
        const row = this.getRowByKey(rowID);
×
UNCOV
926
        return super.unpinRow(rowID, row);
×
927
    }
928

929
    /** @hidden */
930
    public generateRowPath(rowId: any): any[] {
UNCOV
931
        const path: any[] = [];
×
UNCOV
932
        let record = this.records.get(rowId);
×
933

UNCOV
934
        while (record.parent) {
×
UNCOV
935
            path.push(record.parent.key);
×
UNCOV
936
            record = record.parent;
×
937
        }
938

UNCOV
939
        return path.reverse();
×
940
    }
941

942
    /** @hidden */
943
    public isTreeRow(record: any): boolean {
UNCOV
944
        return record.key !== undefined && record.data;
×
945
    }
946

947
    /** @hidden */
948
    public override getUnpinnedIndexById(id) {
UNCOV
949
        return this.unpinnedRecords.findIndex(x => x.data[this.primaryKey] === id);
×
950
    }
951

952
    /**
953
     * @hidden
954
     */
955
    public createRow(index: number, data?: any): RowType {
956
        let row: RowType;
UNCOV
957
        const dataIndex = this._getDataViewIndex(index);
×
UNCOV
958
        const rec: any = data ?? this.dataView[dataIndex];
×
959

UNCOV
960
        if (this.isSummaryRow(rec)) {
×
UNCOV
961
            row = new IgxSummaryRow(this as any, index, rec.summaries);
×
962
        }
963

UNCOV
964
        if (!row && rec) {
×
UNCOV
965
            const isTreeRow = this.isTreeRow(rec);
×
UNCOV
966
            const dataRec = isTreeRow ? rec.data : rec;
×
UNCOV
967
            const treeRow = isTreeRow ? rec : undefined;
×
UNCOV
968
            row = new IgxTreeGridRow(this as any, index, dataRec, treeRow);
×
969
        }
970

UNCOV
971
        return row;
×
972
    }
973

974
    protected override generateDataFields(data: any[]): string[] {
UNCOV
975
        return super.generateDataFields(data).filter(field => field !== this.childDataKey);
×
976
    }
977

978
    protected override transactionStatusUpdate(event: StateUpdateEvent) {
UNCOV
979
        let actions = [];
×
UNCOV
980
        if (event.origin === TransactionEventOrigin.REDO) {
×
UNCOV
981
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.DELETE) : [];
×
UNCOV
982
            if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
983
                this.handleCascadeSelection(event);
×
984
            }
UNCOV
985
        } else if (event.origin === TransactionEventOrigin.UNDO) {
×
UNCOV
986
            actions = event.actions ? event.actions.filter(x => x.transaction.type === TransactionType.ADD) : [];
×
UNCOV
987
            if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
UNCOV
988
                if (event.actions[0].transaction.type === 'add') {
×
UNCOV
989
                    const rec = this.gridAPI.get_rec_by_id(event.actions[0].transaction.id);
×
UNCOV
990
                    this.handleCascadeSelection(event, rec);
×
991
                } else {
UNCOV
992
                    this.handleCascadeSelection(event);
×
993
                }
994
            }
995
        }
UNCOV
996
        if (actions.length) {
×
UNCOV
997
            for (const action of actions) {
×
UNCOV
998
                this.deselectChildren(action.transaction.id);
×
999
            }
1000
        }
UNCOV
1001
        super.transactionStatusUpdate(event);
×
1002
    }
1003

1004
    protected findRecordIndexInView(rec) {
1005
        return this.dataView.findIndex(x => x.data[this.primaryKey] === rec[this.primaryKey]);
×
1006
    }
1007

1008
    /**
1009
     * @hidden @internal
1010
     */
1011
    protected override getDataBasedBodyHeight(): number {
UNCOV
1012
        return !this.flatData || (this.flatData.length < this._defaultTargetRecordNumber) ?
×
1013
            0 : this.defaultTargetBodyHeight;
1014
    }
1015

1016
    /**
1017
     * @hidden
1018
     */
1019
    protected override scrollTo(row: any | number, column: any | number): void {
UNCOV
1020
        let delayScrolling = false;
×
1021
        let record: ITreeGridRecord;
1022

UNCOV
1023
        if (typeof (row) !== 'number') {
×
UNCOV
1024
            const rowData = row;
×
UNCOV
1025
            const rowID = this.gridAPI.get_row_id(rowData);
×
UNCOV
1026
            record = this.processedRecords.get(rowID);
×
UNCOV
1027
            this.gridAPI.expand_path_to_record(record);
×
1028

UNCOV
1029
            if (this.paginator) {
×
UNCOV
1030
                const rowIndex = this.processedExpandedFlatData.indexOf(rowData);
×
UNCOV
1031
                const page = Math.floor(rowIndex / this.perPage);
×
1032

UNCOV
1033
                if (this.page !== page) {
×
UNCOV
1034
                    delayScrolling = true;
×
UNCOV
1035
                    this.page = page;
×
1036
                }
1037
            }
1038
        }
1039

UNCOV
1040
        if (delayScrolling) {
×
UNCOV
1041
            this.verticalScrollContainer.dataChanged.pipe(first()).subscribe(() => {
×
UNCOV
1042
                this.scrollDirective(this.verticalScrollContainer,
×
1043
                    typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(record));
×
1044
            });
1045
        } else {
UNCOV
1046
            this.scrollDirective(this.verticalScrollContainer,
×
1047
                typeof (row) === 'number' ? row : this.unpinnedDataView.indexOf(record));
×
1048
        }
1049

UNCOV
1050
        this.scrollToHorizontally(column);
×
1051
    }
1052

1053
    protected override writeToData(rowIndex: number, value: any) {
1054
        mergeObjects(this.flatData[rowIndex], value);
×
1055
    }
1056

1057
    /**
1058
     * @hidden
1059
     */
1060
    protected override initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
UNCOV
1061
        if (this.hasColumnLayouts) {
×
1062
            // invalid configuration - tree grid should not allow column layouts
1063
            // remove column layouts
1064
            const nonColumnLayoutColumns = this.columns.filter((col) => !col.columnLayout && !col.columnLayoutChild);
×
1065
            this.updateColumns(nonColumnLayoutColumns);
×
1066
        }
UNCOV
1067
        super.initColumns(collection, cb);
×
1068
    }
1069

1070
    /**
1071
     * @hidden @internal
1072
     */
1073
    protected override getGroupAreaHeight(): number {
UNCOV
1074
        return this.treeGroupArea ? this.getComputedHeight(this.treeGroupArea.nativeElement) : 0;
×
1075
    }
1076

1077
    /** {@link triggerPipes} will re-create pinnedData on CRUD operations */
1078
    protected trackPinnedRowData(record: ITreeGridRecord) {
1079
        // TODO FIX: pipeline data doesn't match end interface (¬_¬ )
1080
        // return record.key || (record as any).rowID;
UNCOV
1081
        return record;
×
1082
    }
1083

1084
    /**
1085
     * @description A recursive way to deselect all selected children of a given record
1086
     * @param recordID ID of the record whose children to deselect
1087
     * @hidden
1088
     * @internal
1089
     */
1090
    private deselectChildren(recordID): void {
UNCOV
1091
        const selectedChildren = [];
×
1092
        // G.E. Apr 28, 2021 #9465 Records which are not in view can also be selected so we need to
1093
        // deselect them as well, hence using 'records' map instead of getRowByKey() method which will
1094
        // return only row components (i.e. records in view).
UNCOV
1095
        const rowToDeselect = this.records.get(recordID);
×
UNCOV
1096
        this.selectionService.deselectRowsWithNoEvent([recordID]);
×
UNCOV
1097
        this.gridAPI.get_selected_children(rowToDeselect, selectedChildren);
×
UNCOV
1098
        if (selectedChildren.length > 0) {
×
UNCOV
1099
            selectedChildren.forEach(x => this.deselectChildren(x));
×
1100
        }
1101
    }
1102

1103
    private addChildRows(children: any[], parentID: any) {
UNCOV
1104
        if (this.primaryKey && this.foreignKey) {
×
UNCOV
1105
            for (const child of children) {
×
UNCOV
1106
                child[this.foreignKey] = parentID;
×
1107
            }
UNCOV
1108
            this.data.push(...children);
×
UNCOV
1109
        } else if (this.childDataKey) {
×
UNCOV
1110
            let parent = this.records.get(parentID);
×
UNCOV
1111
            let parentData = parent.data;
×
1112

UNCOV
1113
            if (this.transactions.enabled && this.transactions.getAggregatedChanges(true).length) {
×
1114
                const path = [];
×
1115
                while (parent) {
×
1116
                    path.push(parent.key);
×
1117
                    parent = parent.parent;
×
1118
                }
1119

1120
                let collection = this.data;
×
1121
                let record: any;
1122
                for (let i = path.length - 1; i >= 0; i--) {
×
1123
                    const pid = path[i];
×
1124
                    record = collection.find(r => r[this.primaryKey] === pid);
×
1125

1126
                    if (!record) {
×
1127
                        break;
×
1128
                    }
1129
                    collection = record[this.childDataKey];
×
1130
                }
1131
                if (record) {
×
1132
                    parentData = record;
×
1133
                }
1134
            }
1135

UNCOV
1136
            parentData[this.childDataKey] = children;
×
1137
        }
UNCOV
1138
        this.selectionService.clearHeaderCBState();
×
UNCOV
1139
        this.pipeTrigger++;
×
UNCOV
1140
        if (this.rowSelection === GridSelectionMode.multipleCascade) {
×
1141
            // Force pipe triggering for building the data structure
UNCOV
1142
            this.cdr.detectChanges();
×
UNCOV
1143
            if (this.selectionService.isRowSelected(parentID)) {
×
UNCOV
1144
                this.selectionService.rowSelection.delete(parentID);
×
UNCOV
1145
                this.selectionService.selectRowsWithNoEvent([parentID]);
×
1146
            }
1147
        }
1148
    }
1149

1150
    private loadChildrenOnRowExpansion(args: IRowToggleEventArgs) {
UNCOV
1151
        if (this.loadChildrenOnDemand) {
×
UNCOV
1152
            const parentID = args.rowID;
×
1153

UNCOV
1154
            if (args.expanded && !this._expansionStates.has(parentID)) {
×
UNCOV
1155
                this.loadingRows.add(parentID);
×
1156

UNCOV
1157
                this.loadChildrenOnDemand(parentID, children => {
×
UNCOV
1158
                    this.loadingRows.delete(parentID);
×
UNCOV
1159
                    this.addChildRows(children, parentID);
×
UNCOV
1160
                    this.notifyChanges();
×
1161
                });
1162
            }
1163
        }
1164
    }
1165

1166
    private handleCascadeSelection(event: IRowDataEventArgs | StateUpdateEvent, rec: ITreeGridRecord = null) {
×
1167
        // Wait for the change detection to update records through the pipes
UNCOV
1168
        requestAnimationFrame(() => {
×
UNCOV
1169
            if (rec === null) {
×
UNCOV
1170
                rec = this.gridAPI.get_rec_by_id((event as StateUpdateEvent).actions[0].transaction.id);
×
1171
            }
UNCOV
1172
            if (rec && rec.parent) {
×
UNCOV
1173
                this.gridAPI.grid.selectionService.updateCascadeSelectionOnFilterAndCRUD(
×
1174
                    new Set([rec.parent]), rec.parent.key
1175
                );
UNCOV
1176
                this.notifyChanges();
×
1177
            }
1178
        });
1179
    }
1180
}
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