• 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

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, NgIf, NgClass, NgFor, 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;
2✔
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
        NgIf,
137
        NgFor,
138
        NgClass,
139
        NgStyle,
140
        NgTemplateOutlet,
141
        IgxGridHeaderRowComponent,
142
        IgxGridBodyDirective,
143
        IgxGridDragSelectDirective,
144
        IgxColumnMovingDropDirective,
145
        IgxGridForOfDirective,
146
        IgxTemplateOutletDirective,
147
        IgxTreeGridRowComponent,
148
        IgxSummaryRowComponent,
149
        IgxOverlayOutletDirective,
150
        IgxToggleDirective,
151
        IgxCircularProgressBarComponent,
152
        IgxSnackbarComponent,
153
        IgxButtonDirective,
154
        IgxRippleDirective,
155
        IgxRowEditTabStopDirective,
156
        IgxIconComponent,
157
        IgxGridColumnResizerComponent,
158
        IgxHasVisibleColumnsPipe,
159
        IgxGridRowPinningPipe,
160
        IgxGridRowClassesPipe,
161
        IgxGridRowStylesPipe,
162
        IgxSummaryDataPipe,
163
        IgxTreeGridHierarchizingPipe,
164
        IgxTreeGridFlatteningPipe,
165
        IgxTreeGridSortingPipe,
166
        IgxTreeGridFilteringPipe,
167
        IgxTreeGridPagingPipe,
168
        IgxTreeGridTransactionPipe,
169
        IgxTreeGridSummaryPipe,
170
        IgxTreeGridNormalizeRecordsPipe,
171
        IgxTreeGridAddRowPipe,
172
        IgxStringReplacePipe
173
    ],
174
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
175
})
176
export class IgxTreeGridComponent extends IgxGridBaseDirective implements GridType, OnInit, AfterViewInit, DoCheck, AfterContentInit {
2✔
177
    /**
178
     * Sets the child data key of the `IgxTreeGridComponent`.
179
     * ```html
180
     * <igx-tree-grid #grid [data]="employeeData" [childDataKey]="'employees'" [autoGenerate]="true"></igx-tree-grid>
181
     * ```
182
     *
183
     * @memberof IgxTreeGridComponent
184
     */
185
    @Input()
186
    public childDataKey: string;
187

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
941
        return path.reverse();
×
942
    }
943

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

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

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

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

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

UNCOV
973
        return row;
×
974
    }
975

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

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

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

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

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

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

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

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

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

UNCOV
1052
        this.scrollToHorizontally(column);
×
1053
    }
1054

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

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

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

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

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

UNCOV
1108
            if (this.transactions.enabled && this.transactions.getAggregatedChanges(true).length) {
×
1109
                const path = [];
×
1110
                while (parent) {
×
1111
                    path.push(parent.key);
×
1112
                    parent = parent.parent;
×
1113
                }
1114

1115
                let collection = this.data;
×
1116
                let record: any;
1117
                for (let i = path.length - 1; i >= 0; i--) {
×
1118
                    const pid = path[i];
×
1119
                    record = collection.find(r => r[this.primaryKey] === pid);
×
1120

1121
                    if (!record) {
×
1122
                        break;
×
1123
                    }
1124
                    collection = record[this.childDataKey];
×
1125
                }
1126
                if (record) {
×
1127
                    parentData = record;
×
1128
                }
1129
            }
1130

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

1145
    private loadChildrenOnRowExpansion(args: IRowToggleEventArgs) {
UNCOV
1146
        if (this.loadChildrenOnDemand) {
×
UNCOV
1147
            const parentID = args.rowID;
×
1148

UNCOV
1149
            if (args.expanded && !this._expansionStates.has(parentID)) {
×
UNCOV
1150
                this.loadingRows.add(parentID);
×
1151

UNCOV
1152
                this.loadChildrenOnDemand(parentID, children => {
×
UNCOV
1153
                    this.loadingRows.delete(parentID);
×
UNCOV
1154
                    this.addChildRows(children, parentID);
×
UNCOV
1155
                    this.notifyChanges();
×
1156
                });
1157
            }
1158
        }
1159
    }
1160

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