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

IgniteUI / igniteui-angular / 16002290942

01 Jul 2025 02:31PM UTC coverage: 91.409% (+0.003%) from 91.406%
16002290942

Pull #16009

github

web-flow
Merge pull request #15995 from IgniteUI/dkamburov/fix-37933

fix(grid): Cancel endEdit if there is no active editing
Pull Request #16009: Mass merging 20.0.x to master

13414 of 15750 branches covered (85.17%)

25 of 26 new or added lines in 3 files covered. (96.15%)

13 existing lines in 2 files now uncovered.

27111 of 29659 relevant lines covered (91.41%)

36638.34 hits per line

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

92.16
/projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.ts
1
import {
2
    AfterContentInit,
3
    AfterViewInit,
4
    booleanAttribute,
5
    ChangeDetectionStrategy,
6
    ChangeDetectorRef,
7
    Component,
8
    ContentChildren,
9
    CUSTOM_ELEMENTS_SCHEMA,
10
    DoCheck,
11
    ElementRef,
12
    HostBinding,
13
    Inject,
14
    Input,
15
    OnDestroy,
16
    OnInit,
17
    QueryList,
18
    reflectComponentType,
19
    SimpleChanges,
20
    TemplateRef,
21
    ViewChild,
22
    ViewChildren,
23
    ViewContainerRef
24
} from '@angular/core';
25
import { NgClass, NgTemplateOutlet, NgStyle } from '@angular/common';
26

27
import { IgxHierarchicalGridAPIService } from './hierarchical-grid-api.service';
28
import { IgxRowIslandComponent } from './row-island.component';
29
import { IgxFilteringService } from '../filtering/grid-filtering.service';
30
import { IgxColumnComponent, } from '../columns/column.component';
31
import { IgxHierarchicalGridNavigationService } from './hierarchical-grid-navigation.service';
32
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
33
import { IgxHierarchicalGridBaseDirective } from './hierarchical-grid-base.directive';
34
import { takeUntil } from 'rxjs/operators';
35
import { IgxTemplateOutletDirective } from '../../directives/template-outlet/template_outlet.directive';
36
import { IgxGridSelectionService } from '../selection/selection.service';
37
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
38
import { CellType, EntityType, FieldType, GridType, IGX_GRID_BASE, IGX_GRID_SERVICE_BASE, RowType } from '../common/grid.interface';
39
import { IgxRowIslandAPIService } from './row-island-api.service';
40
import { IgxGridCRUDService } from '../common/crud.service';
41
import { IgxHierarchicalGridRow } from '../grid-public-row';
42
import { IgxGridCell } from '../grid-public-cell';
43
import type { IgxPaginatorComponent } from '../../paginator/paginator.component';
44
import { IgxPaginatorToken } from '../../paginator/token';
45
import { IgxGridComponent } from '../grid/grid.component';
46
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive';
47
import { IgxColumnResizingService } from '../resizing/resizing.service';
48
import { IgxGridExcelStyleFilteringComponent } from '../filtering/excel-style/excel-style-filtering.component';
49
import { IgxGridValidationService } from '../grid/grid-validation.service';
50
import { IgxGridHierarchicalPipe, IgxGridHierarchicalPagingPipe } from './hierarchical-grid.pipes';
51
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
52
import { IgxGridTransactionPipe, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridAddRowPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxStringReplacePipe } from '../common/pipes';
53
import { IgxGridSortingPipe, IgxGridFilteringPipe } from '../grid/grid.pipes';
54
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
55
import { IgxRowEditTabStopDirective } from '../grid.rowEdit.directive';
56
import { IgxIconComponent } from '../../icon/icon.component';
57
import { IgxRippleDirective } from '../../directives/ripple/ripple.directive';
58
import { IgxButtonDirective } from '../../directives/button/button.directive';
59
import { IgxSummaryRowComponent } from '../summaries/summary-row.component';
60
import { IgxSnackbarComponent } from '../../snackbar/snackbar.component';
61
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
62
import { IgxHierarchicalRowComponent } from './hierarchical-row.component';
63
import { IgxGridForOfDirective } from '../../directives/for-of/for_of.directive';
64
import { IgxColumnMovingDropDirective } from '../moving/moving.drop.directive';
65
import { IgxGridDragSelectDirective } from '../selection/drag-select.directive';
66
import { IgxGridBodyDirective } from '../grid.common';
67
import { IgxGridHeaderRowComponent } from '../headers/grid-header-row.component';
68
import { IgxActionStripToken } from '../../action-strip/token';
69
import { flatten } from '../../core/utils';
70
import { IFilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree';
71

72
let NEXT_ID = 0;
3✔
73

74
/**
75
 * @hidden @internal
76
 */
77
@Component({
78
    changeDetection: ChangeDetectionStrategy.OnPush,
79
    selector: 'igx-child-grid-row',
80
    templateUrl: './child-grid-row.component.html',
81
    imports: [NgClass]
82
})
83
export class IgxChildGridRowComponent implements AfterViewInit, OnInit {
3✔
84
    @Input()
85
    public layout: IgxRowIslandComponent;
86

87
    /**
88
     * @hidden
89
     */
90
    public get parentHasScroll() {
91
        return !this.parentGrid.verticalScrollContainer.dc.instance.notVirtual;
2,722✔
92
    }
93

94

95
    /**
96
     * @hidden
97
     */
98
    @Input()
99
    public parentGridID: string;
100

101
    /**
102
     *  The data passed to the row component.
103
     *
104
     * ```typescript
105
     * // get the row data for the first selected row
106
     * let selectedRowData = this.grid.selectedRows[0].data;
107
     * ```
108
     */
109
    @Input()
110
    public get data(): any {
111
        return this._data || [];
4,519!
112
    }
113

114
    public set data(value: any) {
115
        this._data = value;
731✔
116
        if (this.hGrid && !this.hGrid.dataSetByUser) {
731✔
117
            this.hGrid.setDataInternal(this._data.childGridsData[this.layout.key]);
85✔
118
        }
119
    }
120

121
    /**
122
     * The index of the row.
123
     *
124
     * ```typescript
125
     * // get the index of the second selected row
126
     * let selectedRowIndex = this.grid.selectedRows[1].index;
127
     * ```
128
     */
129
    @Input()
130
    public index: number;
131

132
    /* blazorSuppress */
133
    @ViewChild('container', { read: ViewContainerRef, static: true })
134
    public container: ViewContainerRef;
135

136
    /**
137
     * @hidden
138
     */
139
    public hGrid: IgxHierarchicalGridComponent;
140

141
    /* blazorSuppress */
142
    /**
143
     * Get a reference to the grid that contains the selected row.
144
     *
145
     * ```typescript
146
     * handleRowSelection(event) {
147
     *  // the grid on which the rowSelected event was triggered
148
     *  const grid = event.row.grid;
149
     * }
150
     * ```
151
     *
152
     * ```html
153
     *  <igx-grid
154
     *    [data]="data"
155
     *    (rowSelected)="handleRowSelection($event)">
156
     *  </igx-grid>
157
     * ```
158
     */
159
    // TODO: Refactor
160
    public get parentGrid(): IgxHierarchicalGridComponent {
161
        return this.gridAPI.grid as IgxHierarchicalGridComponent;
4,012✔
162
    }
163

164
    @HostBinding('attr.aria-level')
165
    public get level() {
166
        return this.layout.level;
5,349✔
167
    }
168

169
    /**
170
     * The native DOM element representing the row. Could be null in certain environments.
171
     *
172
     * ```typescript
173
     * // get the nativeElement of the second selected row
174
     * let selectedRowNativeElement = this.grid.selectedRows[1].nativeElement;
175
     * ```
176
     */
177
    public get nativeElement() {
178
        return this.element.nativeElement;
3✔
179
    }
180

181
    /**
182
     * Returns whether the row is expanded.
183
     * ```typescript
184
     * const RowExpanded = this.grid1.rowList.first.expanded;
185
     * ```
186
     */
187
    public expanded = false;
645✔
188

189
    private _data: any;
190

191
    constructor(
192
        @Inject(IGX_GRID_SERVICE_BASE) public readonly gridAPI: IgxHierarchicalGridAPIService,
645✔
193
        public element: ElementRef<HTMLElement>,
645✔
194
        public cdr: ChangeDetectorRef) { }
645✔
195

196
    /**
197
     * @hidden
198
     */
199
    public ngOnInit() {
200
        const ref = this.container.createComponent(IgxHierarchicalGridComponent, { injector: this.container.injector });
645✔
201
        this.hGrid = ref.instance;
645✔
202
        this.hGrid.setDataInternal(this.data.childGridsData[this.layout.key]);
645✔
203
        this.hGrid.nativeElement["__componentRef"] = ref;
645✔
204
        this.layout.layoutChange.subscribe((ch) => {
645✔
205
            this._handleLayoutChanges(ch);
55✔
206
        });
207
        const changes = this.layout.initialChanges;
645✔
208
        changes.forEach(change => {
645✔
209
            this._handleLayoutChanges(change);
677✔
210
        });
211
        this.hGrid.parent = this.parentGrid;
645✔
212
        this.hGrid.parentIsland = this.layout;
645✔
213
        this.hGrid.childRow = this;
645✔
214
        // handler logic that re-emits hgrid events on the row island
215
        this.setupEventEmitters();
645✔
216
        this.layout.gridCreated.emit({
645✔
217
            owner: this.layout,
218
            parentID: this.data.rowID,
219
            grid: this.hGrid,
220
            parentRowData: this.data.parentRowData,
221
        });
222
    }
223

224
    /**
225
     * @hidden
226
     */
227
    public ngAfterViewInit() {
228
        this.hGrid.childLayoutList = this.layout.children;
645✔
229
        const layouts = this.hGrid.childLayoutList.toArray();
645✔
230
        layouts.forEach((l) => this.hGrid.gridAPI.registerChildRowIsland(l));
645✔
231
        this.parentGrid.gridAPI.registerChildGrid(this.data.rowID, this.layout.key, this.hGrid);
645✔
232
        this.layout.rowIslandAPI.registerChildGrid(this.data.rowID, this.hGrid);
645✔
233

234
        this.layout.gridInitialized.emit({
645✔
235
            owner: this.layout,
236
            parentID: this.data.rowID,
237
            grid: this.hGrid,
238
            parentRowData: this.data.parentRowData,
239
        });
240

241
        this.hGrid.cdr.detectChanges();
645✔
242
    }
243

244
    private setupEventEmitters() {
245
        const destructor = takeUntil(this.hGrid.destroy$);
645✔
246

247
        const mirror = reflectComponentType(IgxGridComponent);
645✔
248
        // exclude outputs related to two-way binding functionality
249
        const inputNames = mirror.inputs.map(input => input.propName);
51,600✔
250
        const outputs = mirror.outputs.filter(o => {
645✔
251
            const matchingInputPropName = o.propName.slice(0, o.propName.indexOf('Change'));
38,055✔
252
            return inputNames.indexOf(matchingInputPropName) === -1;
38,055✔
253
        });
254

255
        // TODO: Skip the `rendered` output. Rendered should be called once per grid.
256
        outputs.filter(o => o.propName !== 'rendered').forEach(output => {
32,250✔
257
            if (this.hGrid[output.propName]) {
31,605✔
258
                this.hGrid[output.propName].pipe(destructor).subscribe((args) => {
30,960✔
259
                    if (!args) {
5,095!
260
                        args = {};
×
261
                    }
262
                    args.owner = this.hGrid;
5,095✔
263
                    this.layout[output.propName].emit(args);
5,095✔
264
                });
265
            }
266
        });
267
    }
268

269

270
    private _handleLayoutChanges(changes: SimpleChanges) {
271
        for (const change in changes) {
732✔
272
            if (changes.hasOwnProperty(change)) {
2,159✔
273
                this.hGrid[change] = changes[change].currentValue;
2,159✔
274
            }
275
        }
276
    }
277
}
278

279

280
/* blazorAdditionalDependency: Column */
281
/* blazorAdditionalDependency: ColumnGroup */
282
/* blazorAdditionalDependency: ColumnLayout */
283
/* blazorAdditionalDependency: GridToolbar */
284
/* blazorAdditionalDependency: GridToolbarActions */
285
/* blazorAdditionalDependency: GridToolbarTitle */
286
/* blazorAdditionalDependency: GridToolbarAdvancedFiltering */
287
/* blazorAdditionalDependency: GridToolbarExporter */
288
/* blazorAdditionalDependency: GridToolbarHiding */
289
/* blazorAdditionalDependency: GridToolbarPinning */
290
/* blazorAdditionalDependency: ActionStrip */
291
/* blazorAdditionalDependency: GridActionsBaseDirective */
292
/* blazorAdditionalDependency: GridEditingActions */
293
/* blazorAdditionalDependency: GridPinningActions */
294
/* blazorAdditionalDependency: RowIsland */
295
/* blazorIndirectRender */
296
/**
297
 * Hierarchical grid
298
 *
299
 * @igxModule IgxHierarchicalGridModule
300
 *
301
 */
302
@Component({
303
    changeDetection: ChangeDetectionStrategy.OnPush,
304
    selector: 'igx-hierarchical-grid',
305
    templateUrl: 'hierarchical-grid.component.html',
306
    providers: [
307
        IgxGridCRUDService,
308
        IgxGridValidationService,
309
        IgxGridSelectionService,
310
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxHierarchicalGridAPIService },
311
        { provide: IGX_GRID_BASE, useExisting: IgxHierarchicalGridComponent },
312
        IgxGridSummaryService,
313
        IgxFilteringService,
314
        IgxHierarchicalGridNavigationService,
315
        IgxColumnResizingService,
316
        IgxForOfSyncService,
317
        IgxForOfScrollSyncService,
318
        IgxRowIslandAPIService
319
    ],
320
    imports: [
321
        NgClass,
322
        NgTemplateOutlet,
323
        NgStyle,
324
        IgxGridHeaderRowComponent,
325
        IgxGridBodyDirective,
326
        IgxGridDragSelectDirective,
327
        IgxColumnMovingDropDirective,
328
        IgxGridForOfDirective,
329
        IgxTemplateOutletDirective,
330
        IgxHierarchicalRowComponent,
331
        IgxOverlayOutletDirective,
332
        IgxToggleDirective,
333
        IgxCircularProgressBarComponent,
334
        IgxSnackbarComponent,
335
        IgxSummaryRowComponent,
336
        IgxButtonDirective,
337
        IgxRippleDirective,
338
        IgxIconComponent,
339
        IgxRowEditTabStopDirective,
340
        IgxGridColumnResizerComponent,
341
        IgxChildGridRowComponent,
342
        IgxGridSortingPipe,
343
        IgxGridFilteringPipe,
344
        IgxGridTransactionPipe,
345
        IgxHasVisibleColumnsPipe,
346
        IgxGridRowPinningPipe,
347
        IgxGridAddRowPipe,
348
        IgxGridRowClassesPipe,
349
        IgxGridRowStylesPipe,
350
        IgxSummaryDataPipe,
351
        IgxGridHierarchicalPipe,
352
        IgxGridHierarchicalPagingPipe,
353
        IgxStringReplacePipe
354
    ],
355
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
356
})
357
export class IgxHierarchicalGridComponent extends IgxHierarchicalGridBaseDirective
3✔
358
    implements GridType, AfterViewInit, AfterContentInit, OnInit, OnDestroy, DoCheck {
359

360
    /**
361
     * @hidden @internal
362
     */
363
    @HostBinding('attr.role')
364
    public role = 'grid';
912✔
365

366
    /* contentChildren */
367
    /* blazorInclude */
368
    /* blazorTreatAsCollection */
369
    /* blazorCollectionName: RowIslandCollection */
370
    /* ngQueryListName: childLayoutList */
371
    /**
372
     * @hidden
373
     */
374
    @ContentChildren(IgxRowIslandComponent, { read: IgxRowIslandComponent, descendants: false })
375
    public childLayoutList: QueryList<IgxRowIslandComponent>;
376

377
    /**
378
     * @hidden
379
     */
380
    @ContentChildren(IgxRowIslandComponent, { read: IgxRowIslandComponent, descendants: true })
381
    public allLayoutList: QueryList<IgxRowIslandComponent>;
382

383
    /** @hidden @internal */
384
    @ContentChildren(IgxPaginatorToken, { descendants: true })
385
    public paginatorList: QueryList<IgxPaginatorComponent>;
386

387
    /** @hidden @internal */
388
    @ViewChild('toolbarOutlet', { read: ViewContainerRef })
389
    public toolbarOutlet: ViewContainerRef;
390

391
    /** @hidden @internal */
392
    @ViewChild('paginatorOutlet', { read: ViewContainerRef })
393
    public paginatorOutlet: ViewContainerRef;
394
    /**
395
     * @hidden
396
     */
397
    @ViewChildren(IgxTemplateOutletDirective, { read: IgxTemplateOutletDirective })
398
    public templateOutlets: QueryList<any>;
399

400
    /**
401
     * @hidden
402
     */
403
    @ViewChildren(IgxChildGridRowComponent)
404
    public hierarchicalRows: QueryList<IgxChildGridRowComponent>;
405

406
    @ViewChild('hierarchical_record_template', { read: TemplateRef, static: true })
407
    protected hierarchicalRecordTemplate: TemplateRef<any>;
408

409
    @ViewChild('child_record_template', { read: TemplateRef, static: true })
410
    protected childTemplate: TemplateRef<any>;
411

412
    // @ViewChild('headerHierarchyExpander', { read: ElementRef, static: true })
413
    protected get headerHierarchyExpander() {
414
        return this.theadRow?.headerHierarchyExpander;
29,199✔
415
    }
416

417
    /**
418
     * @hidden
419
     */
420
    public childLayoutKeys = [];
912✔
421

422
    /** @hidden @internal */
423
    public dataSetByUser = false;
912✔
424

425
    /**
426
     * @hidden
427
     */
428
    public highlightedRowID = null;
912✔
429

430
    /**
431
     * @hidden
432
     */
433
    public updateOnRender = false;
912✔
434

435
    /**
436
     * @hidden
437
     */
438
    public parent: IgxHierarchicalGridComponent = null;
912✔
439

440
    /**
441
     * @hidden @internal
442
     */
443
    public childRow: IgxChildGridRowComponent;
444

445
    @ContentChildren(IgxActionStripToken, { read: IgxActionStripToken, descendants: false })
446
    protected override actionStripComponents: QueryList<IgxActionStripToken>;
447

448
    /** @hidden @internal */
449
    public override get actionStrip() {
450
        return this.parentIsland ? this.parentIsland.actionStrip : super.actionStrip;
1,070✔
451
    }
452

453
    public override get advancedFilteringExpressionsTree(): IFilteringExpressionsTree {
454
        return super.advancedFilteringExpressionsTree;
15,188✔
455
    }
456

457
    public override set advancedFilteringExpressionsTree(value: IFilteringExpressionsTree) {
458
        if (!this._hGridSchema) {
17✔
459
            this._hGridSchema = this.generateSchema();
12✔
460
        }
461
        super.advancedFilteringExpressionsTree = value;
17✔
462
    }
463

464
    private _data;
465
    private h_id = `igx-hierarchical-grid-${NEXT_ID++}`;
912✔
466
    private childGridTemplates: Map<any, any> = new Map();
912✔
467

468
    /**
469
     * Gets/Sets the value of the `id` attribute.
470
     *
471
     * @remarks
472
     * If not provided it will be automatically generated.
473
     * @example
474
     * ```html
475
     * <igx-hierarchical-grid [id]="'igx-hgrid-1'" [data]="Data" [autoGenerate]="true"></igx-hierarchical-grid>
476
     * ```
477
     */
478
    @HostBinding('attr.id')
479
    @Input()
480
    public get id(): string {
481
        return this.h_id;
1,063,887✔
482
    }
483
    public set id(value: string) {
484
        this.h_id = value;
×
485
    }
486

487
    /* treatAsRef */
488
    /**
489
     * Gets/Sets the array of data that populates the component.
490
     * ```html
491
     * <igx-hierarchical-grid [data]="Data" [autoGenerate]="true"></igx-hierarchical-grid>
492
     * ```
493
     *
494
     * @memberof IgxHierarchicalGridComponent
495
     */
496
    @Input()
497
    public set data(value: any[] | null) {
498
        this.setDataInternal(value);
311✔
499
        this.dataSetByUser = true;
311✔
500
        this.checkPrimaryKeyField();
311✔
501
    }
502

503
    /**
504
     * Returns an array of data set to the `IgxHierarchicalGridComponent`.
505
     * ```typescript
506
     * let filteredData = this.grid.filteredData;
507
     * ```
508
     *
509
     * @memberof IgxHierarchicalGridComponent
510
     */
511
    public get data(): any[] | null {
512
        return this._data;
61,265✔
513
    }
514

515
    /** @hidden @internal */
516
    public override get paginator() {
517
        const id = this.id;
806,096✔
518
        return (!this.parentIsland && this.paginationComponents?.first) || this.rootGrid.paginatorList?.find((pg) =>
806,096✔
519
            pg.nativeElement.offsetParent?.id === id);
90,942✔
520
    }
521

522
    /** @hidden @internal */
523
    public override get excelStyleFilteringComponent(): IgxGridExcelStyleFilteringComponent {
524
        return this.parentIsland ?
2✔
525
            this.parentIsland.excelStyleFilteringComponents.first :
526
            super.excelStyleFilteringComponent;
527
    }
528

529
    /**
530
     * Gets/Sets the total number of records in the data source.
531
     *
532
     * @remarks
533
     * This property is required for remote grid virtualization to function when it is bound to remote data.
534
     * @example
535
     * ```typescript
536
     * const itemCount = this.grid1.totalItemCount;
537
     * this.grid1.totalItemCount = 55;
538
     * ```
539
     */
540
    @Input()
541
    public set totalItemCount(count) {
542
        this.verticalScrollContainer.totalItemCount = count;
×
543
    }
544

545
    public get totalItemCount() {
546
        return this.verticalScrollContainer.totalItemCount;
83✔
547
    }
548

549
    /**
550
     * Sets if all immediate children of the `IgxHierarchicalGridComponent` should be expanded/collapsed.
551
     * Default value is false.
552
     * ```html
553
     * <igx-hierarchical-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true" [expandChildren]="true"></igx-hierarchical-grid>
554
     * ```
555
     *
556
     * @memberof IgxHierarchicalGridComponent
557
     */
558
    @Input({ transform: booleanAttribute })
559
    public set expandChildren(value: boolean) {
560
        this._defaultExpandState = value;
98✔
561
        this.expansionStates = new Map<any, boolean>();
98✔
562
    }
563

564
    /**
565
     * Gets if all immediate children of the `IgxHierarchicalGridComponent` previously have been set to be expanded/collapsed.
566
     * If previously set and some rows have been manually expanded/collapsed it will still return the last set value.
567
     * ```typescript
568
     * const expanded = this.grid.expandChildren;
569
     * ```
570
     *
571
     * @memberof IgxHierarchicalGridComponent
572
     */
573
    public get expandChildren(): boolean {
574
        return this._defaultExpandState;
137,736✔
575
    }
576

577
    /* blazorSuppress */
578
    /**
579
     * Gets/Sets the schema for the hierarchical grid.
580
     * This schema defines the structure and properties of the data displayed in the grid.
581
     * @Input()
582
     * @param {EntityType[]} entities - An array of EntityType objects representing the grid's schema.
583
     * @remarks
584
     * This property is required in remote data filtering scenarios.
585
     * @example
586
     * ```typescript
587
     * const schema = this.grid.schema;
588
     * this.grid.schema = [{ name: 'Products', fields: [...], childEntities: [...] }];
589
     * ```
590
     */
591
    @Input()
592
    public set schema(entities: EntityType[]) {
593
        this._hGridSchema = entities;
1✔
594
    }
595

596
    /* blazorSuppress */
597
    public get schema() {
598
        if (!this._hGridSchema) {
2,977✔
599
            this._hGridSchema = this.generateSchema();
11✔
600
        }
601

602
        return this._hGridSchema;
2,977✔
603
    }
604

605
    /**
606
     * Gets the unique identifier of the parent row. It may be a `string` or `number` if `primaryKey` of the
607
     * parent grid is set or an object reference of the parent record otherwise.
608
     * ```typescript
609
     * const foreignKey = this.grid.foreignKey;
610
     * ```
611
     *
612
     * @memberof IgxHierarchicalGridComponent
613
     */
614
    public get foreignKey() {
615
        if (!this.parent) {
×
616
            return null;
×
617
        }
618
        return this.parent.gridAPI.getParentRowId(this);
×
619
    }
620

621
    /**
622
     * @hidden
623
     */
624
    public get hasExpandableChildren() {
625
        return !!this.childLayoutKeys.length;
26,981✔
626
    }
627

628
    /**
629
     * @hidden
630
     */
631
    public get resolveRowEditContainer() {
632
        if (this.parentIsland && this.parentIsland.rowEditCustom) {
1,681!
633
            return this.parentIsland.rowEditContainer;
×
634
        }
635
        return this.rowEditContainer;
1,681✔
636
    }
637

638
    /**
639
     * @hidden
640
     */
641
    public get resolveRowEditActions() {
642
        return this.parentIsland ? this.parentIsland.rowEditActionsTemplate : this.rowEditActionsTemplate;
1,681✔
643
    }
644

645
    /**
646
     * @hidden
647
     */
648
    public get resolveRowEditText() {
649
        return this.parentIsland ? this.parentIsland.rowEditTextTemplate : this.rowEditTextTemplate;
1,670✔
650
    }
651

652
    /** @hidden */
653
    public override hideActionStrip() {
654
        if (!this.parent) {
1✔
655
            // hide child layout actions strips when
656
            // moving outside root grid.
657
            super.hideActionStrip();
1✔
658
            this.allLayoutList.forEach(ri => {
1✔
659
                ri.actionStrip?.hide();
2✔
660
            });
661
        }
662
    }
663

664
    /**
665
     * @hidden
666
     */
667
    public override get parentRowOutletDirective() {
668
        // Targeting parent outlet in order to prevent hiding when outlet
669
        // is present at a child grid and is attached to a row.
670
        return this.parent ? this.parent.rowOutletDirective : this.outlet;
1!
671
    }
672

673
    /**
674
     * @hidden
675
     */
676
    public override ngOnInit() {
677
        // this.expansionStatesChange.pipe(takeUntil(this.destroy$)).subscribe((value: Map<any, boolean>) => {
678
        //     const res = Array.from(value.entries()).filter(({1: v}) => v === true).map(([k]) => k);
679
        // });
680
        this.batchEditing = !!this.rootGrid.batchEditing;
912✔
681
        if (this.rootGrid !== this) {
912✔
682
            this.rootGrid.batchEditingChange.pipe(takeUntil(this.destroy$)).subscribe((val: boolean) => {
645✔
683
                this.batchEditing = val;
×
684
            });
685
        }
686
        super.ngOnInit();
912✔
687
    }
688

689
    /**
690
     * @hidden
691
     */
692
    public override ngAfterViewInit() {
693
        super.ngAfterViewInit();
912✔
694
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
912✔
695
            const rowData = view.context.$implicit;
636✔
696
            if (this.isChildGridRecord(rowData)) {
636✔
697
                const cachedData = this.childGridTemplates.get(rowData.rowID);
51✔
698
                if (cachedData) {
51✔
699
                    const tmlpOutlet = cachedData.owner;
43✔
700
                    tmlpOutlet._viewContainerRef.detach(0);
43✔
701
                }
702
            }
703
        });
704

705
        if (this.parent) {
912✔
706
            this.childLayoutKeys = this.parentIsland.children.map((item) => item.key);
645✔
707
        }
708

709
        this.headSelectorsTemplates = this.parentIsland ?
912✔
710
            this.parentIsland.headSelectorsTemplates :
711
            this.headSelectorsTemplates;
712

713
        this.rowSelectorsTemplates = this.parentIsland ?
912✔
714
            this.parentIsland.rowSelectorsTemplates :
715
            this.rowSelectorsTemplates;
716
        this.dragIndicatorIconTemplate = this.parentIsland ?
912✔
717
            this.parentIsland.dragIndicatorIconTemplate :
718
            this.dragIndicatorIconTemplate;
719
        this.rowExpandedIndicatorTemplate = this.rootGrid.rowExpandedIndicatorTemplate;
912✔
720
        this.rowCollapsedIndicatorTemplate = this.rootGrid.rowCollapsedIndicatorTemplate;
912✔
721
        this.headerCollapsedIndicatorTemplate = this.rootGrid.headerCollapsedIndicatorTemplate;
912✔
722
        this.headerExpandedIndicatorTemplate = this.rootGrid.headerExpandedIndicatorTemplate;
912✔
723
        this.excelStyleHeaderIconTemplate = this.rootGrid.excelStyleHeaderIconTemplate;
912✔
724
        this.sortAscendingHeaderIconTemplate = this.rootGrid.sortAscendingHeaderIconTemplate;
912✔
725
        this.sortDescendingHeaderIconTemplate = this.rootGrid.sortDescendingHeaderIconTemplate;
912✔
726
        this.sortHeaderIconTemplate = this.rootGrid.sortHeaderIconTemplate;
912✔
727
        this.hasChildrenKey = this.parentIsland ?
912✔
728
            this.parentIsland.hasChildrenKey || this.rootGrid.hasChildrenKey :
1,290✔
729
            this.rootGrid.hasChildrenKey;
730
        this.showExpandAll = this.parentIsland ?
912✔
731
            this.parentIsland.showExpandAll : this.rootGrid.showExpandAll;
732
    }
733

734
    /**
735
     * @hidden
736
     */
737
    public override ngAfterContentInit() {
738
        this.updateColumnList(false);
912✔
739
        this.childLayoutKeys = this.parent ?
912✔
740
            this.parentIsland.children.map((item) => item.key) :
443✔
741
            this.childLayoutKeys = this.childLayoutList.map((item) => item.key);
311✔
742
        this.childLayoutList.notifyOnChanges();
912✔
743
        this.childLayoutList.changes.pipe(takeUntil(this.destroy$)).subscribe(() =>
912✔
744
            this.onRowIslandChange()
3✔
745
        );
746
        super.ngAfterContentInit();
912✔
747
    }
748

749
    /**
750
     * Returns the `RowType` by index.
751
     *
752
     * @example
753
     * ```typescript
754
     * const myRow = this.grid1.getRowByIndex(1);
755
     * ```
756
     * @param index
757
     */
758
    public getRowByIndex(index: number): RowType {
759
        if (index < 0 || index >= this.dataView.length) {
53✔
760
            return undefined;
1✔
761
        }
762
        return this.createRow(index);
52✔
763
    }
764

765
    /**
766
     * Returns the `RowType` by key.
767
     *
768
     * @example
769
     * ```typescript
770
     * const myRow = this.grid1.getRowByKey(1);
771
     * ```
772
     * @param key
773
     */
774
    public getRowByKey(key: any): RowType {
775
        const data = this.dataView;
27✔
776
        const rec = this.primaryKey ?
27✔
777
            data.find(record => record[this.primaryKey] === key) :
106✔
778
            data.find(record => record === key);
20✔
779
        const index = data.indexOf(rec);
27✔
780
        if (index < 0 || index > data.length) {
27✔
781
            return undefined;
1✔
782
        }
783

784
        return new IgxHierarchicalGridRow(this as any, index, rec);
26✔
785
    }
786

787
    /**
788
     * @hidden @internal
789
     */
790
    public allRows(): RowType[] {
791
        return this.dataView.map((rec, index) => this.createRow(index));
34✔
792
    }
793

794
    /**
795
     * Returns the collection of `IgxHierarchicalGridRow`s for current page.
796
     *
797
     * @hidden @internal
798
     */
799
    public dataRows(): RowType[] {
800
        return this.allRows().filter(row => row instanceof IgxHierarchicalGridRow);
34✔
801
    }
802

803
    /**
804
     * Returns an array of the selected `IgxGridCell`s.
805
     *
806
     * @example
807
     * ```typescript
808
     * const selectedCells = this.grid.selectedCells;
809
     * ```
810
     */
811
    public get selectedCells(): CellType[] {
812
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
168✔
813
            .reduce((a, b) => a.concat(b), []);
34✔
814
    }
815

816
    /**
817
     * Returns a `CellType` object that matches the conditions.
818
     *
819
     * @example
820
     * ```typescript
821
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
822
     * ```
823
     * @param rowIndex
824
     * @param columnField
825
     */
826
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
827
        const row = this.getRowByIndex(rowIndex);
13✔
828
        const column = this.columns.find((col) => col.field === columnField);
17✔
829
        if (row && row instanceof IgxHierarchicalGridRow && column) {
13✔
830
            return new IgxGridCell(this, rowIndex, column);
13✔
831
        }
832
    }
833

834
    /**
835
     * Returns a `CellType` object that matches the conditions.
836
     *
837
     * @remarks
838
     * Requires that the primaryKey property is set.
839
     * @example
840
     * ```typescript
841
     * grid.getCellByKey(1, 'index');
842
     * ```
843
     * @param rowSelector match any rowID
844
     * @param columnField
845
     */
846
    public getCellByKey(rowSelector: any, columnField: string): CellType {
847
        const row = this.getRowByKey(rowSelector);
1✔
848
        const column = this.columns.find((col) => col.field === columnField);
7✔
849
        if (row && column) {
1✔
850
            return new IgxGridCell(this, row.index, column);
1✔
851
        }
852
    }
853

854
    public override pinRow(rowID: any, index?: number): boolean {
855
        const row = this.getRowByKey(rowID);
18✔
856
        return super.pinRow(rowID, index, row);
18✔
857
    }
858

859
    /** @hidden @internal */
860
    public setDataInternal(value: any) {
861
        const oldData = this._data;
1,041✔
862
        this._data = value || [];
1,041✔
863
        this.summaryService.clearSummaryCache();
1,041✔
864
        if (!this._init) {
1,041✔
865
            this.validation.updateAll(this._data);
129✔
866
        }
867
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
1,041✔
868
            this.setupColumns();
9✔
869
            this.reflow();
9✔
870
        }
871
        this.cdr.markForCheck();
1,041✔
872
        if (this.parent && (this.height === null || this.height.indexOf('%') !== -1)) {
1,041✔
873
            // If the height will change based on how much data there is, recalculate sizes in igxForOf.
874
            this.notifyChanges(true);
85✔
875
        }
876
    }
877

878
    public override unpinRow(rowID: any): boolean {
879
        const row = this.getRowByKey(rowID);
1✔
880
        return super.unpinRow(rowID, row);
1✔
881
    }
882

883
    /**
884
     * @hidden @internal
885
     */
886
    public dataLoading(event) {
887
        this.dataPreLoad.emit(event);
48✔
888
    }
889

890
    /** @hidden */
891
    public override featureColumnsWidth() {
892
        return super.featureColumnsWidth(this.headerHierarchyExpander);
29,195✔
893
    }
894

895
    /**
896
     * @hidden
897
     */
898
    public onRowIslandChange() {
899
        if (this.parent) {
743✔
900
            this.childLayoutKeys = this.parentIsland.children.filter(item => !(item as any)._destroyed).map((item) => item.key);
433✔
901
        } else {
902
            this.childLayoutKeys = this.childLayoutList.filter(item => !(item as any)._destroyed).map((item) => item.key);
399✔
903
        }
904
        if (!(this.cdr as any).destroyed) {
743✔
905
            this.cdr.detectChanges();
100✔
906
        }
907
    }
908

909
    /** @hidden @internal **/
910
    public override ngOnDestroy() {
911
        if (!this.parent) {
877✔
912
            this.gridAPI.getChildGrids(true).forEach((grid) => {
262✔
913
                if (!grid.childRow.cdr.destroyed) {
×
914
                    grid.childRow.cdr.destroy();
×
915
                }
916
            });
917
        }
918
        if (this.parent && this.selectionService.activeElement) {
877✔
919
            // in case selection is in destroyed child grid, selection should be cleared.
920
            this._clearSeletionHighlights();
31✔
921
        }
922
        super.ngOnDestroy();
877✔
923
    }
924

925
    /**
926
     * @hidden
927
     */
928
    public isRowHighlighted(rowData) {
929
        return this.highlightedRowID === rowData.rowID;
4,660✔
930
    }
931

932
    /**
933
     * @hidden
934
     */
935
    public isHierarchicalRecord(record: any): boolean {
936
        if (this.isGhostRecord(record)) {
56,022✔
937
            record = record.recordRef;
94✔
938
        }
939
        return this.childLayoutList.length !== 0 && record[this.childLayoutList.first.key];
56,022✔
940
    }
941

942
    /**
943
     * @hidden
944
     */
945
    public isChildGridRecord(record: any): boolean {
946
        // Can be null when there is defined layout but no child data was found
947
        return record?.childGridsData !== undefined;
78,364✔
948
    }
949

950
    /**
951
     * @hidden
952
     */
953
    public trackChanges(index, rec) {
954
        if (rec.childGridsData !== undefined) {
1,761,786✔
955
            // if is child rec
956
            return rec.rowID;
16,203✔
957
        }
958
        return rec;
1,745,583✔
959
    }
960

961
    /**
962
     * @hidden
963
     */
964
    public getContext(rowData, rowIndex, pinned): any {
965
        if (this.isChildGridRecord(rowData)) {
56,124✔
966
            const cachedData = this.childGridTemplates.get(rowData.rowID);
4,691✔
967
            if (cachedData) {
4,691✔
968
                const view = cachedData.view;
4,124✔
969
                const tmlpOutlet = cachedData.owner;
4,124✔
970
                return {
4,124✔
971
                    $implicit: rowData,
972
                    moveView: view,
973
                    owner: tmlpOutlet,
974
                    index: this.dataView.indexOf(rowData)
975
                };
976
            } else {
977
                // child rows contain unique grids, hence should have unique templates
978
                return {
567✔
979
                    $implicit: rowData,
980
                    templateID: {
981
                        type: 'childRow',
982
                        id: rowData.rowID
983
                    },
984
                    index: this.dataView.indexOf(rowData)
985
                };
986
            }
987
        } else {
988
            return {
51,433✔
989
                $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
51,433✔
990
                templateID: {
991
                    type: 'dataRow',
992
                    id: null
993
                },
994
                index: this.getDataViewIndex(rowIndex, pinned),
995
                disabled: this.isGhostRecord(rowData)
996
            };
997
        }
998
    }
999

1000
    /**
1001
     * @hidden
1002
     */
1003
    public get rootGrid(): GridType {
1004
        let currGrid = this as IgxHierarchicalGridComponent;
878,074✔
1005
        while (currGrid.parent) {
878,074✔
1006
            currGrid = currGrid.parent;
641,721✔
1007
        }
1008
        return currGrid;
878,074✔
1009
    }
1010

1011
    /**
1012
     * @hidden
1013
     */
1014
    public get iconTemplate() {
1015
        const expanded = this.hasExpandedRecords() && this.hasExpandableChildren;
11,206✔
1016
        if (!expanded && this.showExpandAll) {
11,206✔
1017
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
88✔
1018
        } else {
1019
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
11,118✔
1020
        }
1021
    }
1022

1023
    /**
1024
     * @hidden
1025
     * @internal
1026
     */
1027
    public override getDragGhostCustomTemplate(): TemplateRef<any> {
1028
        if (this.parentIsland) {
140✔
1029
            return this.parentIsland.getDragGhostCustomTemplate();
53✔
1030
        }
1031
        return super.getDragGhostCustomTemplate();
87✔
1032
    }
1033

1034
    /**
1035
     * @hidden
1036
     * Gets the visible content height that includes header + tbody + footer.
1037
     * For hierarchical child grid it may be scrolled and not fully visible.
1038
     */
1039
    public override getVisibleContentHeight() {
1040
        let height = super.getVisibleContentHeight();
×
1041
        if (this.parent) {
×
1042
            const rootHeight = this.rootGrid.getVisibleContentHeight();
×
1043
            const topDiff = this.nativeElement.getBoundingClientRect().top - this.rootGrid.nativeElement.getBoundingClientRect().top;
×
1044
            height = rootHeight - topDiff > height ? height : rootHeight - topDiff;
×
1045
        }
1046
        return height;
×
1047
    }
1048

1049
    /**
1050
     * @hidden
1051
     */
1052
    public toggleAll() {
1053
        const expanded = this.hasExpandedRecords() && this.hasExpandableChildren;
1✔
1054
        if (!expanded && this.showExpandAll) {
1!
1055
            this.expandAll();
×
1056
        } else {
1057
            this.collapseAll();
1✔
1058
        }
1059
    }
1060

1061

1062
    /**
1063
     * @hidden
1064
     * @internal
1065
     */
1066
    public hasExpandedRecords() {
1067
        if (this.expandChildren) {
22,388✔
1068
            return true;
2,178✔
1069
        }
1070
        let hasExpandedEntry = false;
20,210✔
1071
        this.expansionStates.forEach(value => {
20,210✔
1072
            if (value) {
2,862✔
1073
                hasExpandedEntry = value;
2,691✔
1074
            }
1075
        });
1076
        return hasExpandedEntry;
20,210✔
1077
    }
1078

1079
    public override getDefaultExpandState(record: any) {
1080
        if (this.hasChildrenKey && !record[this.hasChildrenKey]) {
115,382✔
1081
            return false;
34✔
1082
        }
1083
        return this.expandChildren;
115,348✔
1084

1085
    }
1086

1087
    /**
1088
     * @hidden
1089
     */
1090
    public isExpanded(record: any): boolean {
1091
        return this.gridAPI.get_row_expansion_state(record);
×
1092
    }
1093

1094
    /**
1095
     * @hidden
1096
     */
1097
    public viewCreatedHandler(args) {
1098
        if (this.isChildGridRecord(args.context.$implicit)) {
6,540✔
1099
            const key = args.context.$implicit.rowID;
567✔
1100
            this.childGridTemplates.set(key, args);
567✔
1101
        }
1102
    }
1103

1104
    /**
1105
     * @hidden
1106
     */
1107
    public viewMovedHandler(args) {
1108
        if (this.isChildGridRecord(args.context.$implicit)) {
49✔
1109
            // view was moved, update owner in cache
1110
            const key = args.context.$implicit.rowID;
49✔
1111
            const cachedData = this.childGridTemplates.get(key);
49✔
1112
            cachedData.owner = args.owner;
49✔
1113

1114
            this.childLayoutList.forEach((layout) => {
49✔
1115
                const relatedGrid = this.gridAPI.getChildGridByID(layout.key, args.context.$implicit.rowID);
52✔
1116
                if (relatedGrid && relatedGrid.updateOnRender) {
52✔
1117
                    // Detect changes if `expandChildren` has changed when the grid wasn't visible. This is for performance reasons.
1118
                    relatedGrid.notifyChanges(true);
1✔
1119
                    relatedGrid.updateOnRender = false;
1✔
1120
                }
1121
            });
1122
        }
1123
    }
1124

1125
    /** @hidden @internal **/
1126
    public onContainerScroll() {
1127
        this.hideOverlays();
×
1128
    }
1129

1130
    /**
1131
     * @hidden
1132
     */
1133
    public createRow(index: number, data?: any): RowType {
1134
        let row: RowType;
1135
        const dataIndex = this._getDataViewIndex(index);
340✔
1136
        const rec: any = data ?? this.dataView[dataIndex];
340✔
1137

1138
        if (!row && rec && !rec.childGridsData) {
340✔
1139
            row = new IgxHierarchicalGridRow(this as any, index, rec);
340✔
1140
        }
1141

1142
        return row;
340✔
1143
    }
1144

1145
    /** @hidden @internal */
1146
    public getChildGrids(inDeph?: boolean) {
1147
        return this.gridAPI.getChildGrids(inDeph);
139✔
1148
    }
1149

1150
    protected override generateDataFields(data: any[]): string[] {
1151
        return super.generateDataFields(data).filter((field) => {
374✔
1152
            const layoutsList = this.parentIsland ? this.parentIsland.children : this.childLayoutList;
2,832✔
1153
            const keys = layoutsList.map((item) => item.key);
2,832✔
1154
            return keys.indexOf(field) === -1;
2,832✔
1155
        });
1156
    }
1157

1158
    protected resizeNotifyHandler() {
1159
        // do not trigger reflow if element is detached or if it is child grid.
1160
        if (this.nativeElement?.isConnected && !this.parent) {
×
1161
            this.notifyChanges(true);
×
1162
        }
1163
    }
1164

1165
    /**
1166
     * @hidden
1167
     */
1168
    protected override initColumns(collection: IgxColumnComponent[], cb: (args: any) => void = null) {
×
1169
        if (this.hasColumnLayouts) {
784!
1170
            // invalid configuration - hierarchical grid should not allow column layouts
1171
            // remove column layouts
1172
            const nonColumnLayoutColumns = this.columns.filter((col) => !col.columnLayout && !col.columnLayoutChild);
×
1173
            this.updateColumns(nonColumnLayoutColumns);
×
1174
        }
1175
        super.initColumns(collection, cb);
784✔
1176
    }
1177

1178

1179
    protected override setupColumns() {
1180
        if (this.parentIsland && this.parentIsland.childColumns.length > 0 && !this.autoGenerate) {
921✔
1181
            this.createColumnsList(this.parentIsland.childColumns.toArray());
347✔
1182
        } else {
1183
            super.setupColumns();
574✔
1184
        }
1185
    }
1186

1187
    protected override getColumnList() {
1188
        const childLayouts = this.parent ? this.childLayoutList : this.allLayoutList;
200!
1189
        const nestedColumns = childLayouts.map((layout) => layout.columnList.toArray());
403✔
1190
        const colsArray = [].concat.apply([], nestedColumns);
200✔
1191
        if (colsArray.length > 0) {
200✔
1192
            const topCols = this.columnList.filter((item) => colsArray.indexOf(item) === -1);
1,970✔
1193
            return topCols;
193✔
1194
        } else {
1195
            return this.columnList.toArray()
7✔
1196
        }
1197
    }
1198

1199
    protected override onColumnsChanged() {
1200
        Promise.resolve().then(() => {
11✔
1201
            this.updateColumnList();
11✔
1202
        });
1203
    }
1204

1205
    protected override _shouldAutoSize(renderedHeight) {
1206
        if (this.isPercentHeight && this.parent) {
755✔
1207
            return true;
755✔
1208
        }
1209
        return super._shouldAutoSize(renderedHeight);
×
1210
    }
1211

1212
    private updateColumnList(recalcColSizes = true) {
11✔
1213
        const childLayouts = this.parent ? this.childLayoutList : this.allLayoutList;
923✔
1214
        const nestedColumns = childLayouts.map((layout) => layout.columnList.toArray());
923✔
1215
        const colsArray = [].concat.apply([], nestedColumns);
923✔
1216
        const colLength = this.columns.length;
923✔
1217
        const topCols = this.columnList.filter((item) => colsArray.indexOf(item) === -1);
2,093✔
1218
        if (topCols.length > 0) {
923✔
1219
            this.initColumns(topCols, (col: IgxColumnComponent) => this.columnInit.emit(col));
697✔
1220
            if (recalcColSizes && this.columns.length !== colLength) {
210✔
1221
                this.calculateGridSizes(false);
3✔
1222
            }
1223
        }
1224
    }
1225

1226
    private _clearSeletionHighlights() {
1227
        [this.rootGrid, ...this.rootGrid.getChildGrids(true)].forEach(grid => {
31✔
1228
            grid.selectionService.clear();
227✔
1229
            grid.selectionService.activeElement = null;
227✔
1230
            grid.nativeElement.classList.remove('igx-grid__tr--highlighted');
227✔
1231
            grid.highlightedRowID = null;
227✔
1232
            grid.cdr.markForCheck();
227✔
1233
        });
1234
    }
1235

1236
    private generateSchema() {
1237
        const filterableFields = this.columns.filter((column) => !column.columnGroup && column.filterable);
97✔
1238
        let entities: EntityType[];
1239

1240
        if(filterableFields.length !== 0) {
23✔
1241
            entities = [
23✔
1242
                {
1243
                    name: null,
1244
                    fields: filterableFields.map(f => ({
68✔
1245
                            field: f.field,
1246
                            dataType: f.dataType,
1247
                        //  label: f.label,
1248
                        //  header: f.header,
1249
                            editorOptions: f.editorOptions,
1250
                            filters: f.filters,
1251
                            pipeArgs: f.pipeArgs,
1252
                            defaultTimeFormat: f.defaultTimeFormat,
1253
                            defaultDateTimeFormat: f.defaultDateTimeFormat
1254
                        })) as FieldType[]
1255
                }
1256
            ];
1257

1258
            entities[0].childEntities = this.childLayoutList.reduce((acc, rowIsland) => {
23✔
1259
                return acc.concat(this.generateChildEntity(rowIsland, this.data[0][rowIsland.key][0]));
24✔
1260
            }
1261
            , []);
1262
        }
1263

1264
        return entities;
23✔
1265
    }
1266

1267
    private generateChildEntity(rowIsland: IgxRowIslandComponent, firstRowData: any[]): EntityType {
1268
        const entityName = rowIsland.key;
38✔
1269
        let fields = [];
38✔
1270
        let childEntities;
1271
        if (!rowIsland.autoGenerate) {
38✔
1272
            fields = flatten(rowIsland.childColumns.toArray()).filter(col => col.field)
104✔
1273
                .map(f => ({ field: f.field, dataType: f.dataType })) as FieldType[];
86✔
1274
        } else if (firstRowData) {
12✔
1275
            const rowIslandFields = Object.keys(firstRowData).map(key => {
12✔
1276
                if (firstRowData[key] instanceof Array) {
96✔
1277
                    return null;
24✔
1278
                }
1279

1280
                return {
72✔
1281
                    field: key,
1282
                    dataType: this.resolveDataTypes(firstRowData[key])
1283
                }
1284
            });
1285
            fields = rowIslandFields.filter(f => f !== null) as FieldType[];
96✔
1286
        }
1287

1288
        const rowIslandChildEntities = rowIsland.childLayoutList.reduce((acc, childRowIsland) => {
38✔
1289
            if (!firstRowData) {
14!
UNCOV
1290
                return null;
×
1291
            }
1292
            return acc.concat(this.generateChildEntity(childRowIsland, firstRowData[childRowIsland.key][0]));
14✔
1293
        }, []);
1294

1295
        if (rowIslandChildEntities?.length > 0) {
38✔
1296
            childEntities = rowIslandChildEntities;
14✔
1297
        }
1298

1299
        return {
38✔
1300
            name: entityName,
1301
            fields: fields,
1302
            childEntities: childEntities
1303
        }
1304
    }
1305
}
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