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

IgniteUI / igniteui-angular / 16193550997

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

Pull #16028

github

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

178 of 15764 branches covered (1.13%)

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

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

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

0.7
/projects/igniteui-angular/src/lib/grids/grid/grid.component.ts
1
import {
2
    Component, ChangeDetectionStrategy, Input, Output, EventEmitter, ContentChild, ViewChildren,
3
    QueryList, ViewChild, TemplateRef, DoCheck, AfterContentInit, HostBinding,
4
    OnInit, AfterViewInit, ContentChildren, CUSTOM_ELEMENTS_SCHEMA, booleanAttribute
5
} from '@angular/core';
6
import { NgTemplateOutlet, NgClass, NgStyle } from '@angular/common';
7

8
import { IgxGridBaseDirective } from '../grid-base.directive';
9
import { IgxGridNavigationService } from '../grid-navigation.service';
10
import { IgxGridAPIService } from './grid-api.service';
11
import { cloneArray, IBaseEventArgs } from '../../core/utils';
12
import { IGroupByRecord } from '../../data-operations/groupby-record.interface';
13
import { IgxGroupByRowTemplateDirective, IgxGridDetailTemplateDirective } from '../grid.directives';
14
import { IgxGridGroupByRowComponent } from './groupby-row.component';
15
import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.interface';
16
import { IForOfState, IgxGridForOfDirective } from '../../directives/for-of/for_of.directive';
17
import { IgxColumnComponent } from '../columns/column.component';
18
import { take, takeUntil } from 'rxjs/operators';
19
import { IgxFilteringService } from '../filtering/grid-filtering.service';
20
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
21
import { IgxColumnResizingService } from '../resizing/resizing.service';
22
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
23
import { IgxGridSelectionService } from '../selection/selection.service';
24
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
25
import { IgxGridMRLNavigationService } from '../grid-mrl-navigation.service';
26
import { FilterMode } from '../common/enums';
27
import { CellType, GridType, IgxGridMasterDetailContext, IgxGroupByRowSelectorTemplateContext, IgxGroupByRowTemplateContext, IGX_GRID_BASE, IGX_GRID_SERVICE_BASE, RowType } from '../common/grid.interface';
28
import { IgxGroupByRowSelectorDirective } from '../selection/row-selectors';
29
import { IgxGridCRUDService } from '../common/crud.service';
30
import { IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../grid-public-row';
31
import { IgxGridCell } from '../grid-public-cell';
32
import { ISortingExpression } from '../../data-operations/sorting-strategy';
33
import { IGridGroupingStrategy } from '../common/strategy';
34
import { IgxGridValidationService } from './grid-validation.service';
35
import { IgxGridDetailsPipe } from './grid.details.pipe';
36
import { IgxGridSummaryPipe } from './grid.summary.pipe';
37
import { IgxGridGroupingPipe, IgxGridPagingPipe, IgxGridSortingPipe, IgxGridFilteringPipe } from './grid.pipes';
38
import { IgxSummaryDataPipe } from '../summaries/grid-root-summary.pipe';
39
import { IgxGridTransactionPipe, IgxHasVisibleColumnsPipe, IgxGridRowPinningPipe, IgxGridAddRowPipe, IgxGridRowClassesPipe, IgxGridRowStylesPipe, IgxStringReplacePipe } from '../common/pipes';
40
import { IgxGridColumnResizerComponent } from '../resizing/resizer.component';
41
import { IgxRowEditTabStopDirective } from '../grid.rowEdit.directive';
42
import { IgxIconComponent } from '../../icon/icon.component';
43
import { IgxRippleDirective } from '../../directives/ripple/ripple.directive';
44
import { IgxButtonDirective } from '../../directives/button/button.directive';
45
import { IgxSnackbarComponent } from '../../snackbar/snackbar.component';
46
import { IgxCircularProgressBarComponent } from '../../progressbar/progressbar.component';
47
import { IgxOverlayOutletDirective, IgxToggleDirective } from '../../directives/toggle/toggle.directive';
48
import { IgxSummaryRowComponent } from '../summaries/summary-row.component';
49
import { IgxGridRowComponent } from './grid-row.component';
50
import { IgxTemplateOutletDirective } from '../../directives/template-outlet/template_outlet.directive';
51
import { IgxColumnMovingDropDirective } from '../moving/moving.drop.directive';
52
import { IgxGridDragSelectDirective } from '../selection/drag-select.directive';
53
import { IgxGridBodyDirective } from '../grid.common';
54
import { IgxGridHeaderRowComponent } from '../headers/grid-header-row.component';
55
import { IgxGridGroupByAreaComponent } from '../grouping/grid-group-by-area.component';
56
import { Observable, Subject } from 'rxjs';
57

58
let NEXT_ID = 0;
3✔
59

60
export interface IGroupingDoneEventArgs extends IBaseEventArgs {
61
    expressions: Array<ISortingExpression> | ISortingExpression;
62
    groupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
63
    ungroupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
64
}
65

66
/* blazorAdditionalDependency: Column */
67
/* blazorAdditionalDependency: ColumnGroup */
68
/* blazorAdditionalDependency: ColumnLayout */
69
/* blazorAdditionalDependency: GridToolbar */
70
/* blazorAdditionalDependency: GridToolbarActions */
71
/* blazorAdditionalDependency: GridToolbarTitle */
72
/* blazorAdditionalDependency: GridToolbarAdvancedFiltering */
73
/* blazorAdditionalDependency: GridToolbarExporter */
74
/* blazorAdditionalDependency: GridToolbarHiding */
75
/* blazorAdditionalDependency: GridToolbarPinning */
76
/* blazorAdditionalDependency: ActionStrip */
77
/* blazorAdditionalDependency: GridActionsBaseDirective */
78
/* blazorAdditionalDependency: GridEditingActions */
79
/* blazorAdditionalDependency: GridPinningActions */
80
/* blazorIndirectRender */
81
/**
82
 * Grid provides a way to present and manipulate tabular data.
83
 *
84
 * @igxModule IgxGridModule
85
 * @igxGroup Grids & Lists
86
 * @igxKeywords grid, table
87
 * @igxTheme igx-grid-theme
88
 * @remarks
89
 * The Ignite UI Grid is used for presenting and manipulating tabular data in the simplest way possible.  Once data
90
 * has been bound, it can be manipulated through filtering, sorting & editing operations.
91
 * @example
92
 * ```html
93
 * <igx-grid [data]="employeeData" [autoGenerate]="false">
94
 *   <igx-column field="first" header="First Name"></igx-column>
95
 *   <igx-column field="last" header="Last Name"></igx-column>
96
 *   <igx-column field="role" header="Role"></igx-column>
97
 * </igx-grid>
98
 * ```
99
 */
100
@Component({
101
    changeDetection: ChangeDetectionStrategy.OnPush,
102
    preserveWhitespaces: false,
103
    providers: [
104
        IgxGridCRUDService,
105
        IgxGridNavigationService,
106
        IgxGridSummaryService,
107
        IgxGridSelectionService,
108
        IgxGridValidationService,
109
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxGridAPIService },
110
        { provide: IGX_GRID_BASE, useExisting: IgxGridComponent },
111
        IgxFilteringService,
112
        IgxColumnResizingService,
113
        IgxForOfSyncService,
114
        IgxForOfScrollSyncService,
115
    ],
116
    selector: 'igx-grid',
117
    templateUrl: './grid.component.html',
118
    imports: [
119
        NgClass,
120
        NgStyle,
121
        NgTemplateOutlet,
122
        IgxGridGroupByAreaComponent,
123
        IgxGridHeaderRowComponent,
124
        IgxGridBodyDirective,
125
        IgxGridDragSelectDirective,
126
        IgxColumnMovingDropDirective,
127
        IgxGridForOfDirective,
128
        IgxTemplateOutletDirective,
129
        IgxGridRowComponent,
130
        IgxGridGroupByRowComponent,
131
        IgxSummaryRowComponent,
132
        IgxOverlayOutletDirective,
133
        IgxToggleDirective,
134
        IgxCircularProgressBarComponent,
135
        IgxSnackbarComponent,
136
        IgxButtonDirective,
137
        IgxRippleDirective,
138
        IgxIconComponent,
139
        IgxRowEditTabStopDirective,
140
        IgxGridColumnResizerComponent,
141
        IgxGridTransactionPipe,
142
        IgxHasVisibleColumnsPipe,
143
        IgxGridRowPinningPipe,
144
        IgxGridAddRowPipe,
145
        IgxGridRowClassesPipe,
146
        IgxGridRowStylesPipe,
147
        IgxSummaryDataPipe,
148
        IgxGridGroupingPipe,
149
        IgxGridPagingPipe,
150
        IgxGridSortingPipe,
151
        IgxGridFilteringPipe,
152
        IgxGridSummaryPipe,
153
        IgxGridDetailsPipe,
154
        IgxStringReplacePipe
155
    ],
156
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
157
})
158
export class IgxGridComponent extends IgxGridBaseDirective implements GridType, OnInit, DoCheck, AfterContentInit, AfterViewInit {
3✔
159
    /**
160
     * Emitted when a new chunk of data is loaded from virtualization.
161
     *
162
     * @example
163
     * ```typescript
164
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataPreLoad)='handleDataPreloadEvent()'></igx-grid>
165
     * ```
166
     */
167
    @Output()
UNCOV
168
    public dataPreLoad = new EventEmitter<IForOfState>();
×
169

170
    /**
171
     * Emitted when grouping is performed.
172
     *
173
     * @example
174
     * ```html
175
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpressionsChange)="groupingExpressionsChange($event)"></igx-grid>
176
     * ```
177
     */
178
    @Output()
UNCOV
179
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
×
180

181
    /**
182
     * Emitted when groups are expanded/collapsed.
183
     *
184
     * @example
185
     * ```html
186
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpansionStateChange)="groupingExpansionStateChange($event)"></igx-grid>
187
     * ```
188
     */
189
    @Output()
UNCOV
190
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
×
191

192
    /**
193
     * Emitted when columns are grouped/ungrouped.
194
     *
195
     * @remarks
196
     * The `groupingDone` event would be raised only once if several columns get grouped at once by calling
197
     * the `groupBy()` or `clearGrouping()` API methods and passing an array as an argument.
198
     * The event arguments provide the `expressions`, `groupedColumns` and `ungroupedColumns` properties, which contain
199
     * the `ISortingExpression` and the `IgxColumnComponent` related to the grouping/ungrouping operation.
200
     * Please note that `groupedColumns` and `ungroupedColumns` show only the **newly** changed columns (affected by the **last**
201
     * grouping/ungrouping operation), not all columns which are currently grouped/ungrouped.
202
     * columns.
203
     * @example
204
     * ```html
205
     * <igx-grid #grid [data]="localData" (groupingDone)="groupingDone($event)" [autoGenerate]="true"></igx-grid>
206
     * ```
207
     */
208
    @Output()
UNCOV
209
    public groupingDone = new EventEmitter<IGroupingDoneEventArgs>();
×
210

211
    /**
212
     * Gets/Sets whether created groups are rendered expanded or collapsed.
213
     *
214
     * @remarks
215
     * The default rendered state is expanded.
216
     * @example
217
     * ```html
218
     * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid>
219
     * ```
220
     */
221
    @Input({ transform: booleanAttribute })
UNCOV
222
    public groupsExpanded = true;
×
223

224
    /**
225
     * Gets/Sets the template that will be rendered as a GroupBy drop area.
226
     *
227
     * @remarks
228
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
229
     * @example
230
     * ```html
231
     * <igx-grid [dropAreaTemplate]="dropAreaRef">
232
     * </igx-grid>
233
     * <ng-template #myDropArea>
234
     *      <span> Custom drop area! </span>
235
     * </ng-template>
236
     * ```
237
     */
238
    @Input()
239
    public dropAreaTemplate: TemplateRef<void>;
240

241
    /**
242
     * @hidden @internal
243
     */
244
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
245
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
246

247

248
    /**
249
     * Returns a reference to the master-detail template.
250
     * ```typescript
251
     * let detailTemplate = this.grid.detailTemplate;
252
     * ```
253
     *
254
     * @memberof IgxColumnComponent
255
     */
256
    @Input('detailTemplate')
257
    public get detailTemplate(): TemplateRef<IgxGridMasterDetailContext> {
UNCOV
258
        return this._detailTemplate;
×
259
    }
260
    /**
261
     * Sets the master-detail template.
262
     * ```html
263
     * <ng-template #detailTemplate igxGridDetail let-dataItem>
264
     *    <div>
265
     *       <div><span class='categoryStyle'>City:</span> {{dataItem.City}}</div>
266
     *       <div><span class='categoryStyle'>Address:</span> {{dataItem.Address}}</div>
267
     *    </div>
268
     * </ng-template>
269
     * ```
270
     * ```typescript
271
     * @ViewChild("'detailTemplate'", {read: TemplateRef })
272
     * public detailTemplate: TemplateRef<any>;
273
     * this.grid.detailTemplate = this.detailTemplate;
274
     * ```
275
     *
276
     * @memberof IgxColumnComponent
277
     */
278
    public set detailTemplate(template: TemplateRef<IgxGridMasterDetailContext>) {
UNCOV
279
        this._detailTemplate = template;
×
280
    }
281

282
    /**
283
     * @hidden @internal
284
     */
285
    @HostBinding('attr.role')
UNCOV
286
    public role = 'grid';
×
287

288
    /**
289
     * Gets/Sets the value of the `id` attribute.
290
     *
291
     * @remarks
292
     * If not provided it will be automatically generated.
293
     * @example
294
     * ```html
295
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid>
296
     * ```
297
     */
298
    @HostBinding('attr.id')
299
    @Input()
UNCOV
300
    public id = `igx-grid-${NEXT_ID++}`;
×
301

302
    /**
303
     * @hidden @internal
304
     */
305
    @ViewChild('record_template', { read: TemplateRef, static: true })
306
    protected recordTemplate: TemplateRef<any>;
307

308
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
309
    protected detailTemplateContainer: TemplateRef<any>;
310

311
    @ViewChild('group_template', { read: TemplateRef, static: true })
312
    protected defaultGroupTemplate: TemplateRef<any>;
313

314
    @ViewChild('summary_template', { read: TemplateRef, static: true })
315
    protected summaryTemplate: TemplateRef<any>;
316

317
    /**
318
     * @hidden @internal
319
     */
320
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
321
    protected groupTemplate: IgxGroupByRowTemplateDirective;
322

323
    /**
324
     * @hidden
325
     * @internal
326
     */
327
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
328
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
329

330
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
331
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
332

UNCOV
333
    private _groupsRecords: IGroupByRecord[] = [];
×
334
    /**
335
     * Gets the hierarchical representation of the group by records.
336
     *
337
     * @example
338
     * ```typescript
339
     * let groupRecords = this.grid.groupsRecords;
340
     * ```
341
     */
342
    public get groupsRecords(): IGroupByRecord[] {
UNCOV
343
        return this._groupsRecords;
×
344
    }
345

346
    /**
347
     * @hidden @internal
348
     * Includes children of collapsed group rows.
349
     */
350
    public groupingResult: any[];
351

352
    /**
353
     * @hidden @internal
354
     */
355
    public groupingMetadata: any[];
356

357
    /**
358
     * @hidden @internal
359
     * Does not include children of collapsed group rows.
360
     */
361
    public groupingFlatResult: any[];
362
    /**
363
     * @hidden
364
     */
UNCOV
365
    protected _groupingExpressions: IGroupingExpression[] = [];
×
366
    /**
367
     * @hidden
368
     */
UNCOV
369
    protected _groupingExpandState: IGroupByExpandState[] = [];
×
370
    /**
371
     * @hidden
372
     */
373
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
374

375
    /**
376
     * @hidden
377
     */
378
    protected _groupStrategy: IGridGroupingStrategy;
379
    /**
380
     * @hidden
381
     */
382
    protected groupingDiffer;
383
    private _data?: any[] | null;
UNCOV
384
    private _hideGroupedColumns = false;
×
UNCOV
385
    private _dropAreaMessage = null;
×
UNCOV
386
    private _showGroupArea = true;
×
387

388
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
389
    private _detailTemplate;
390

391

392
    /**
393
     * Gets/Sets the array of data that populates the component.
394
     *
395
     * @example
396
     * ```html
397
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
398
     * ```
399
     */
400
    /* treatAsRef */
401
    @Input()
402
    public get data(): any[] | null {
UNCOV
403
        return this._data;
×
404
    }
405

406
    public set data(value: any[] | null) {
UNCOV
407
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
×
UNCOV
408
        const oldData = this._data;
×
UNCOV
409
        this._data = value || [];
×
UNCOV
410
        this.summaryService.clearSummaryCache();
×
UNCOV
411
        if (!this._init) {
×
UNCOV
412
            this.validation.updateAll(this._data);
×
413
        }
414

UNCOV
415
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
×
UNCOV
416
            this.setupColumns();
×
417
        }
418

UNCOV
419
        this.cdr.markForCheck();
×
UNCOV
420
        if (this.isPercentHeight) {
×
UNCOV
421
            this.notifyChanges(true);
×
422
        }
423
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
UNCOV
424
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
×
UNCOV
425
            this.recalculateAutoSizes();
×
426
        }
UNCOV
427
        this.checkPrimaryKeyField();
×
428
    }
429

430
    /**
431
     * Gets/Sets the total number of records in the data source.
432
     *
433
     * @remarks
434
     * This property is required for remote grid virtualization to function when it is bound to remote data.
435
     * @example
436
     * ```typescript
437
     * const itemCount = this.grid1.totalItemCount;
438
     * this.grid1.totalItemCount = 55;
439
     * ```
440
     */
441
    @Input()
442
    public set totalItemCount(count) {
UNCOV
443
        this.verticalScrollContainer.totalItemCount = count;
×
444
    }
445

446
    public get totalItemCount() {
UNCOV
447
        return this.verticalScrollContainer.totalItemCount;
×
448
    }
449

450
    private get _gridAPI(): IgxGridAPIService {
UNCOV
451
        return this.gridAPI as IgxGridAPIService;
×
452
    }
453

UNCOV
454
    private childDetailTemplates: Map<any, any> = new Map();
×
455

456
    /**
457
     * @hidden @internal
458
     */
UNCOV
459
    public groupingPerformedSubject = new Subject<void>();
×
460

461
    /**
462
     * @hidden @internal
463
     */
UNCOV
464
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
×
465

466
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
467
    /**
468
     * Gets/Sets the group by state.
469
     *
470
     * @example
471
     * ```typescript
472
     * let groupByState = this.grid.groupingExpressions;
473
     * this.grid.groupingExpressions = [...];
474
     * ```
475
     * @remarks
476
     * Supports two-way data binding.
477
     * @example
478
     * ```html
479
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid>
480
     * ```
481
     */
482
    @Input()
483
    public get groupingExpressions(): IGroupingExpression[] {
UNCOV
484
        return this._groupingExpressions;
×
485
    }
486

487
    public set groupingExpressions(value: IGroupingExpression[]) {
UNCOV
488
        if (this.groupingExpressions === value) {
×
UNCOV
489
            return;
×
490
        }
UNCOV
491
        if (value && value.length > 10) {
×
UNCOV
492
            throw Error('Maximum amount of grouped columns is 10.');
×
493
        }
UNCOV
494
        const oldExpressions: IGroupingExpression[] = this.groupingExpressions;
×
UNCOV
495
        const newExpressions: IGroupingExpression[] = value;
×
UNCOV
496
        this._groupingExpressions = cloneArray(value);
×
UNCOV
497
        this.groupingExpressionsChange.emit(this._groupingExpressions);
×
UNCOV
498
        if (this._gridAPI.grid) {
×
499
            /* grouping and sorting are working separate from each other */
UNCOV
500
            this._applyGrouping();
×
UNCOV
501
            this.notifyChanges();
×
502
        }
UNCOV
503
        if (!this._init && JSON.stringify(oldExpressions, this.stringifyCallback) !== JSON.stringify(newExpressions, this.stringifyCallback) && this._columns) {
×
UNCOV
504
            const groupedCols: IgxColumnComponent[] = [];
×
UNCOV
505
            const ungroupedCols: IgxColumnComponent[] = [];
×
UNCOV
506
            const groupedColsArr = newExpressions.filter((obj) => !oldExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
×
UNCOV
507
            groupedColsArr.forEach((elem) => {
×
UNCOV
508
                groupedCols.push(this.getColumnByName(elem.fieldName));
×
509
            }, this);
UNCOV
510
            const ungroupedColsArr = oldExpressions.filter((obj) => !newExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
×
UNCOV
511
            ungroupedColsArr.forEach((elem) => {
×
UNCOV
512
                ungroupedCols.push(this.getColumnByName(elem.fieldName));
×
513
            }, this);
UNCOV
514
            this.notifyChanges();
×
UNCOV
515
            const groupingDoneArgs: IGroupingDoneEventArgs = {
×
516
                expressions: newExpressions,
517
                groupedColumns: groupedCols,
518
                ungroupedColumns: ungroupedCols
519
            };
UNCOV
520
            this.groupingPerformed$.pipe(take(1)).subscribe(() => {
×
UNCOV
521
                this.groupingDone.emit(groupingDoneArgs);
×
522
            });
523
        }
524
    }
525

526
    /**
527
     * Gets/Sets a list of expansion states for group rows.
528
     *
529
     * @remarks
530
     * Includes only states that differ from the default one (controlled through groupsExpanded and states that the user has changed.
531
     * Contains the expansion state (expanded: boolean) and the unique identifier for the group row (Array).
532
     * Supports two-way data binding.
533
     * @example
534
     * ```html
535
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpansionState)]="model.groupingExpansionState"></igx-grid>
536
     * ```
537
     */
538
    @Input()
539
    public get groupingExpansionState() {
UNCOV
540
        return this._groupingExpandState;
×
541
    }
542

543
    public set groupingExpansionState(value) {
UNCOV
544
        if (value !== this._groupingExpandState) {
×
UNCOV
545
            this.groupingExpansionStateChange.emit(value);
×
546
        }
UNCOV
547
        this._groupingExpandState = value;
×
UNCOV
548
        if (this.gridAPI.grid) {
×
UNCOV
549
            this.cdr.detectChanges();
×
550
        }
551
    }
552

553
    /**
554
     * Gets/Sets whether the grouped columns should be hidden.
555
     *
556
     * @remarks
557
     * The default value is "false"
558
     * @example
559
     * ```html
560
     * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid>
561
     * ```
562
     */
563
    @Input({ transform: booleanAttribute })
564
    public get hideGroupedColumns() {
UNCOV
565
        return this._hideGroupedColumns;
×
566
    }
567

568
    public set hideGroupedColumns(value: boolean) {
UNCOV
569
        if (value) {
×
UNCOV
570
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
×
571
        } else {
UNCOV
572
            this.groupingDiffer = null;
×
573
        }
UNCOV
574
        if (this._columns && this.groupingExpressions) {
×
UNCOV
575
            this._setGroupColsVisibility(value);
×
576
        }
577

UNCOV
578
        this._hideGroupedColumns = value;
×
579
    }
580

581
    /**
582
     * Gets/Sets the grouping strategy of the grid.
583
     *
584
     * @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
585
     *
586
     * @example
587
     * ```html
588
     *  <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
589
     * ```
590
     */
591
    @Input()
592
    public get groupStrategy(): IGridGroupingStrategy {
UNCOV
593
        return this._groupStrategy;
×
594
    }
595

596
    public set groupStrategy(value: IGridGroupingStrategy) {
UNCOV
597
        this._groupStrategy = value;
×
598
    }
599

600
    /**
601
     * Gets/Sets the message displayed inside the GroupBy drop area where columns can be dragged on.
602
     *
603
     * @remarks
604
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
605
     * @example
606
     * ```html
607
     * <igx-grid dropAreaMessage="Drop here to group!">
608
     *      <igx-column [groupable]="true" field="ID"></igx-column>
609
     * </igx-grid>
610
     * ```
611
     */
612
    @Input()
613
    public set dropAreaMessage(value: string) {
UNCOV
614
        this._dropAreaMessage = value;
×
UNCOV
615
        this.notifyChanges();
×
616
    }
617

618
    public get dropAreaMessage(): string {
UNCOV
619
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
×
620
    }
621

622
    /**
623
     * @hidden @internal
624
     */
625
    public get groupsRowList() {
UNCOV
626
        const res = new QueryList<any>();
×
UNCOV
627
        if (!this._groupsRowList) {
×
628
            return res;
×
629
        }
UNCOV
630
        const rList = this._groupsRowList.filter(item => item.element.nativeElement.parentElement !== null)
×
UNCOV
631
            .sort((item1, item2) => item1.index - item2.index);
×
UNCOV
632
        res.reset(rList);
×
UNCOV
633
        return res;
×
634
    }
635

636
    /**
637
     * Gets the group by row selector template.
638
     */
639
    @Input()
640
    public get groupByRowSelectorTemplate(): TemplateRef<IgxGroupByRowSelectorTemplateContext> {
UNCOV
641
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
×
642
    }
643

644
    /**
645
     * Sets the group by row selector template.
646
     * ```html
647
     * <ng-template #template igxGroupByRowSelector let-groupByRowContext>
648
     * {{ groupByRowContext.selectedCount }} / {{ groupByRowContext.totalCount  }}
649
     * </ng-template>
650
     * ```
651
     * ```typescript
652
     * @ViewChild("'template'", {read: TemplateRef })
653
     * public template: TemplateRef<any>;
654
     * this.grid.groupByRowSelectorTemplate = this.template;
655
     * ```
656
     */
657
    public set groupByRowSelectorTemplate(template: TemplateRef<IgxGroupByRowSelectorTemplateContext>) {
UNCOV
658
        this._groupByRowSelectorTemplate = template;
×
659
    }
660

661
    /**
662
     * @hidden @internal
663
     */
664
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
UNCOV
665
        return {
×
666
            $implicit: rowData,
667
            index
668
        };
669
    }
670

671
    /**
672
     * @hidden @internal
673
     */
674
    public detailsViewFocused(container, rowIndex) {
UNCOV
675
        this.navigation.setActiveNode({ row: rowIndex });
×
676
    }
677

678
    /**
679
     * @hidden @internal
680
     */
681
    public override get hasDetails() {
UNCOV
682
        return !!this.detailTemplate;
×
683
    }
684

685
    /**
686
     * @hidden @internal
687
     */
688
    public getRowTemplate(rowData) {
UNCOV
689
        if (this.isGroupByRecord(rowData)) {
×
UNCOV
690
            return this.defaultGroupTemplate;
×
UNCOV
691
        } else if (this.isSummaryRow(rowData)) {
×
UNCOV
692
            return this.summaryTemplate;
×
UNCOV
693
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
×
UNCOV
694
            return this.detailTemplateContainer;
×
695
        } else {
UNCOV
696
            return this.recordTemplate;
×
697
        }
698
    }
699

700
    /**
701
     * @hidden @internal
702
     */
703
    public override isDetailRecord(record) {
UNCOV
704
        return record && record.detailsData !== undefined;
×
705
    }
706

707
    /**
708
     * @hidden @internal
709
     */
710
    public isDetailActive(rowIndex) {
UNCOV
711
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
×
712
    }
713

714
    /**
715
     * Gets/Sets the template reference for the group row.
716
     *
717
     * @example
718
     * ```
719
     * const groupRowTemplate = this.grid.groupRowTemplate;
720
     * this.grid.groupRowTemplate = myRowTemplate;
721
     * ```
722
     */
723
    @Input()
724
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
UNCOV
725
        return this._groupRowTemplate;
×
726
    }
727

728
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
UNCOV
729
        this._groupRowTemplate = template;
×
UNCOV
730
        this.notifyChanges();
×
731
    }
732

733
    /** @hidden @internal */
734
    public trackChanges: (index, rec) => any;
735

736
    /**
737
     * Groups by a new `IgxColumnComponent` based on the provided expression, or modifies an existing one.
738
     *
739
     * @remarks
740
     * Also allows for multiple columns to be grouped at once if an array of `ISortingExpression` is passed.
741
     * The `groupingDone` event would get raised only **once** if this method gets called multiple times with the same arguments.
742
     * @example
743
     * ```typescript
744
     * this.grid.groupBy({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
745
     * this.grid.groupBy([
746
     *     { fieldName: name1, dir: SortingDirection.Asc, ignoreCase: false },
747
     *     { fieldName: name2, dir: SortingDirection.Desc, ignoreCase: true },
748
     *     { fieldName: name3, dir: SortingDirection.Desc, ignoreCase: false }
749
     * ]);
750
     * ```
751
     */
752
    public groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void {
UNCOV
753
        if (this.checkIfNoColumnField(expression)) {
×
UNCOV
754
            return;
×
755
        }
UNCOV
756
        this.crudService.endEdit(false);
×
UNCOV
757
        if (expression instanceof Array) {
×
UNCOV
758
            this._gridAPI.groupBy_multiple(expression);
×
759
        } else {
UNCOV
760
            this._gridAPI.groupBy(expression);
×
761
        }
UNCOV
762
        this.notifyChanges(true);
×
763
    }
764

765
    /**
766
     * Clears grouping for particular column, array of columns or all columns.
767
     *
768
     * @remarks
769
     * Clears all grouping in the grid, if no parameter is passed.
770
     * If a parameter is provided, clears grouping for a particular column or an array of columns.
771
     * @example
772
     * ```typescript
773
     * this.grid.clearGrouping(); //clears all grouping
774
     * this.grid.clearGrouping("ID"); //ungroups a single column
775
     * this.grid.clearGrouping(["ID", "Column1", "Column2"]); //ungroups multiple columns
776
     * ```
777
     * @param name Name of column or array of column names to be ungrouped.
778
     */
779
    public clearGrouping(name?: string | Array<string>): void {
UNCOV
780
        this._gridAPI.clear_groupby(name);
×
UNCOV
781
        this.calculateGridSizes();
×
UNCOV
782
        this.notifyChanges(true);
×
UNCOV
783
        this.groupingPerformedSubject.next();
×
784
    }
785

786
    /**
787
     * Returns if a group is expanded or not.
788
     *
789
     * @param group The group record.
790
     * @example
791
     * ```typescript
792
     * public groupRow: IGroupByRecord;
793
     * const expandedGroup = this.grid.isExpandedGroup(this.groupRow);
794
     * ```
795
     */
796
    public override isExpandedGroup(group: IGroupByRecord): boolean {
UNCOV
797
        const state: IGroupByExpandState = this._getStateForGroupRow(group);
×
UNCOV
798
        return state ? state.expanded : this.groupsExpanded;
×
799
    }
800

801
    /**
802
     * Toggles the expansion state of a group.
803
     *
804
     * @param groupRow The group record to toggle.
805
     * @example
806
     * ```typescript
807
     * public groupRow: IGroupByRecord;
808
     * const toggleExpGroup = this.grid.toggleGroup(this.groupRow);
809
     * ```
810
     */
811
    public toggleGroup(groupRow: IGroupByRecord) {
UNCOV
812
        this._toggleGroup(groupRow);
×
UNCOV
813
        this.notifyChanges();
×
814
    }
815

816
    /**
817
     * Select all rows within a group.
818
     *
819
     * @param groupRow: The group record which rows would be selected.
820
     * @param clearCurrentSelection if true clears the current selection
821
     * @example
822
     * ```typescript
823
     * this.grid.selectRowsInGroup(this.groupRow, true);
824
     * ```
825
     */
826
    public selectRowsInGroup(groupRow: IGroupByRecord, clearPrevSelection?: boolean) {
UNCOV
827
        this._gridAPI.groupBy_select_all_rows_in_group(groupRow, clearPrevSelection);
×
UNCOV
828
        this.notifyChanges();
×
829
    }
830

831
    /**
832
     * Deselect all rows within a group.
833
     *
834
     * @param groupRow The group record which rows would be deselected.
835
     * @example
836
     * ```typescript
837
     * public groupRow: IGroupByRecord;
838
     * this.grid.deselectRowsInGroup(this.groupRow);
839
     * ```
840
     */
841
    public deselectRowsInGroup(groupRow: IGroupByRecord) {
UNCOV
842
        this._gridAPI.groupBy_deselect_all_rows_in_group(groupRow);
×
UNCOV
843
        this.notifyChanges();
×
844
    }
845

846
    /**
847
     * Expands the specified group and all of its parent groups.
848
     *
849
     * @param groupRow The group record to fully expand.
850
     * @example
851
     * ```typescript
852
     * public groupRow: IGroupByRecord;
853
     * this.grid.fullyExpandGroup(this.groupRow);
854
     * ```
855
     */
856
    public fullyExpandGroup(groupRow: IGroupByRecord) {
857
        this._fullyExpandGroup(groupRow);
×
858
        this.notifyChanges();
×
859
    }
860

861
    /**
862
     * @hidden @internal
863
     */
864
    public override isGroupByRecord(record: any): boolean {
865
        // return record.records instance of GroupedRecords fails under Webpack
UNCOV
866
        return record && record?.records && record.records?.length &&
×
867
            record.expression && record.expression?.fieldName;
868
    }
869

870
    /**
871
     * Toggles the expansion state of all group rows recursively.
872
     *
873
     * @example
874
     * ```typescript
875
     * this.grid.toggleAllGroupRows;
876
     * ```
877
     */
878
    public toggleAllGroupRows() {
UNCOV
879
        this.groupingExpansionState = [];
×
UNCOV
880
        this.groupsExpanded = !this.groupsExpanded;
×
UNCOV
881
        this.notifyChanges();
×
882
    }
883

884
    /** @hidden @internal */
885
    public get hasGroupableColumns(): boolean {
UNCOV
886
        return this._columns.some((col) => col.groupable && !col.columnGroup);
×
887
    }
888

889
    /**
890
     * Returns whether the `IgxGridComponent` has group area.
891
     *
892
     * @example
893
     * ```typescript
894
     * let isGroupAreaVisible = this.grid.showGroupArea;
895
     * ```
896
     *
897
     * @example
898
     * ```html
899
     * <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
900
     * ```
901
     */
902
    @Input({ transform: booleanAttribute })
903
    public get showGroupArea(): boolean {
UNCOV
904
        return this._showGroupArea;
×
905
    }
906
    public set showGroupArea(value: boolean) {
UNCOV
907
        this._showGroupArea = value;
×
UNCOV
908
        this.notifyChanges(true);
×
909
    }
910

911
    /**
912
     * @hidden @internal
913
     */
914
    public override isColumnGrouped(fieldName: string): boolean {
UNCOV
915
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
×
916
    }
917

918
    /**
919
     * @hidden @internal
920
     */
921
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
UNCOV
922
        if (this.isDetailRecord(rowData)) {
×
UNCOV
923
            const cachedData = this.childDetailTemplates.get(rowData.detailsData);
×
UNCOV
924
            const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : rowData.detailsData;
×
UNCOV
925
            if (cachedData) {
×
UNCOV
926
                const view = cachedData.view;
×
UNCOV
927
                const tmlpOutlet = cachedData.owner;
×
UNCOV
928
                return {
×
929
                    $implicit: rowData.detailsData,
930
                    moveView: view,
931
                    owner: tmlpOutlet,
932
                    index: this.dataView.indexOf(rowData),
933
                    templateID: {
934
                        type: 'detailRow',
935
                        id: rowID
936
                    }
937
                };
938
            } else {
939
                // child rows contain unique grids, hence should have unique templates
UNCOV
940
                return {
×
941
                    $implicit: rowData.detailsData,
942
                    templateID: {
943
                        type: 'detailRow',
944
                        id: rowID
945
                    },
946
                    index: this.dataView.indexOf(rowData)
947
                };
948
            }
949
        }
UNCOV
950
        return {
×
951
            $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
×
952
            index: this.getDataViewIndex(rowIndex, pinned),
953
            templateID: {
954
                type: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
×
955
                id: null
956
            },
957
            disabled: this.isGhostRecord(rowData)
958
        };
959
    }
960

961
    /**
962
     * @hidden @internal
963
     */
964
    public viewCreatedHandler(args) {
UNCOV
965
        if (args.context.templateID.type === 'detailRow') {
×
UNCOV
966
            this.childDetailTemplates.set(args.context.$implicit, args);
×
967
        }
968
    }
969

970
    /**
971
     * @hidden @internal
972
     */
973
    public viewMovedHandler(args) {
UNCOV
974
        if (args.context.templateID.type === 'detailRow') {
×
975
            // view was moved, update owner in cache
UNCOV
976
            const key = args.context.$implicit;
×
UNCOV
977
            const cachedData = this.childDetailTemplates.get(key);
×
UNCOV
978
            cachedData.owner = args.owner;
×
979
        }
980
    }
981

982
    /**
983
     * @hidden @internal
984
     */
985
    public get iconTemplate() {
UNCOV
986
        if (this.groupsExpanded) {
×
UNCOV
987
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
×
988
        } else {
UNCOV
989
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
×
990
        }
991
    }
992

993
    /**
994
     * @hidden @internal
995
     */
996
    public override ngAfterContentInit() {
UNCOV
997
        super.ngAfterContentInit();
×
UNCOV
998
        if (this.allowFiltering && this.hasColumnLayouts) {
×
UNCOV
999
            this.filterMode = FilterMode.excelStyleFilter;
×
1000
        }
UNCOV
1001
        if (this.groupTemplate) {
×
UNCOV
1002
            this._groupRowTemplate = this.groupTemplate.template;
×
1003
        }
1004

UNCOV
1005
        if (this.detailTemplateDirective) {
×
UNCOV
1006
            this._detailTemplate = this.detailTemplateDirective;
×
1007
        }
1008

1009

UNCOV
1010
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
×
UNCOV
1011
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1012
        }
UNCOV
1013
        this._setupNavigationService();
×
1014
    }
1015

1016
    /**
1017
     * @hidden @internal
1018
     */
1019
    public override ngAfterViewInit() {
UNCOV
1020
        super.ngAfterViewInit();
×
UNCOV
1021
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
×
UNCOV
1022
            const rowData = view.context.$implicit;
×
UNCOV
1023
            if (this.isDetailRecord(rowData)) {
×
UNCOV
1024
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
×
UNCOV
1025
                if (cachedData) {
×
UNCOV
1026
                    const tmlpOutlet = cachedData.owner;
×
UNCOV
1027
                    tmlpOutlet._viewContainerRef.detach(0);
×
1028
                }
1029
            }
1030
        });
1031

UNCOV
1032
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
×
UNCOV
1033
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
×
UNCOV
1034
                return;
×
1035
            }
1036

UNCOV
1037
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
×
UNCOV
1038
                const fieldName = sortExpr.fieldName;
×
UNCOV
1039
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
×
UNCOV
1040
                if (groupingExpr) {
×
UNCOV
1041
                    groupingExpr.dir = sortExpr.dir;
×
1042
                }
1043
            });
1044
        });
1045
    }
1046

1047
    /**
1048
     * @hidden @internal
1049
     */
1050
    public override ngOnInit() {
UNCOV
1051
        super.ngOnInit();
×
UNCOV
1052
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
×
UNCOV
1053
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
×
UNCOV
1054
            this.crudService.endEdit(false);
×
UNCOV
1055
            this.summaryService.updateSummaryCache(args);
×
UNCOV
1056
            this._headerFeaturesWidth = NaN;
×
1057
        });
1058
    }
1059

1060
    /**
1061
     * @hidden @internal
1062
     */
1063
    public override ngDoCheck(): void {
UNCOV
1064
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
×
UNCOV
1065
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
×
UNCOV
1066
            if (changes && this._columns.length > 0) {
×
UNCOV
1067
                changes.forEachAddedItem((rec) => {
×
UNCOV
1068
                    const col = this.getColumnByName(rec.item.fieldName);
×
UNCOV
1069
                    if (col) {
×
UNCOV
1070
                        col.hidden = true;
×
1071
                    }
1072
                });
UNCOV
1073
                changes.forEachRemovedItem((rec) => {
×
1074
                    const col = this.getColumnByName(rec.item.fieldName);
×
1075
                    col.hidden = false;
×
1076
                });
1077
            }
1078
        }
UNCOV
1079
        super.ngDoCheck();
×
1080
    }
1081

1082
    /**
1083
     * @hidden @internal
1084
     */
1085
    public dataLoading(event) {
UNCOV
1086
        this.dataPreLoad.emit(event);
×
1087
    }
1088

1089
    /**
1090
     *
1091
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
1092
     *
1093
     * @remarks
1094
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
1095
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
1096
     */
1097
    public override getSelectedData(formatters = false, headers = false): any[] {
×
UNCOV
1098
        if (this.groupingExpressions.length || this.hasDetails) {
×
UNCOV
1099
            const source = [];
×
1100

UNCOV
1101
            const process = (record) => {
×
UNCOV
1102
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
×
UNCOV
1103
                    source.push(null);
×
UNCOV
1104
                    return;
×
1105
                }
UNCOV
1106
                source.push(record);
×
1107

1108
            };
1109

UNCOV
1110
            this.dataView.forEach(process);
×
UNCOV
1111
            return this.extractDataFromSelection(source, formatters, headers);
×
1112
        } else {
UNCOV
1113
            return super.getSelectedData(formatters, headers);
×
1114
        }
1115
    }
1116

1117
    /**
1118
     * Returns the `IgxGridRow` by index.
1119
     *
1120
     * @example
1121
     * ```typescript
1122
     * const myRow = grid.getRowByIndex(1);
1123
     * ```
1124
     * @param index
1125
     */
1126
    public getRowByIndex(index: number): RowType {
1127
        let row: RowType;
UNCOV
1128
        if (index < 0) {
×
1129
            return undefined;
×
1130
        }
UNCOV
1131
        if (this.dataView.length >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) {
×
UNCOV
1132
            row = this.createRow(index);
×
1133
        } else {
1134
            if (!(index < this.virtualizationState.startIndex) && !(index > this.virtualizationState.startIndex + this.virtualizationState.chunkSize)) {
×
1135
                row = this.createRow(index);
×
1136
            }
1137
        }
1138

UNCOV
1139
        if (this.pagingMode === 'remote' && this.page !== 0) {
×
UNCOV
1140
            row.index = index + this.perPage * this.page;
×
1141
        }
UNCOV
1142
        return row;
×
1143
    }
1144

1145
    /**
1146
     * Returns `IgxGridRow` object by the specified primary key.
1147
     *
1148
     * @remarks
1149
     * Requires that the `primaryKey` property is set.
1150
     * @example
1151
     * ```typescript
1152
     * const myRow = this.grid1.getRowByKey("cell5");
1153
     * ```
1154
     * @param keyValue
1155
     */
1156
    public getRowByKey(key: any): RowType {
UNCOV
1157
        const rec = this.filteredSortedData ? this.primaryKey ?
×
UNCOV
1158
            this.filteredSortedData.find(record => record[this.primaryKey] === key) :
×
UNCOV
1159
            this.filteredSortedData.find(record => record === key) : undefined;
×
UNCOV
1160
        const index = this.dataView.indexOf(rec);
×
UNCOV
1161
        if (index < 0 || index > this.dataView.length) {
×
UNCOV
1162
            return undefined;
×
1163
        }
1164

UNCOV
1165
        return new IgxGridRow(this, index, rec);
×
1166
    }
1167

1168
    /**
1169
     * @hidden @internal
1170
     */
1171
    public allRows(): RowType[] {
UNCOV
1172
        return this.dataView.map((rec, index) => {
×
UNCOV
1173
            this.pagingMode === 'remote' && this.page !== 0 ?
×
1174
                index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
UNCOV
1175
            return this.createRow(index);
×
1176
        });
1177
    }
1178

1179
    /**
1180
     * Returns the collection of `IgxGridRow`s for current page.
1181
     *
1182
     * @hidden @internal
1183
     */
1184
    public dataRows(): RowType[] {
UNCOV
1185
        return this.allRows().filter(row => row instanceof IgxGridRow);
×
1186
    }
1187

1188
    /**
1189
     * Returns an array of the selected `IgxGridCell`s.
1190
     *
1191
     * @example
1192
     * ```typescript
1193
     * const selectedCells = this.grid.selectedCells;
1194
     * ```
1195
     */
1196
    public get selectedCells(): CellType[] {
UNCOV
1197
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
×
UNCOV
1198
            .reduce((a, b) => a.concat(b), []);
×
1199
    }
1200

1201
    /**
1202
     * Returns a `CellType` object that matches the conditions.
1203
     *
1204
     * @example
1205
     * ```typescript
1206
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
1207
     * ```
1208
     * @param rowIndex
1209
     * @param columnField
1210
     */
1211
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
UNCOV
1212
        const row = this.getRowByIndex(rowIndex);
×
UNCOV
1213
        const column = this._columns.find((col) => col.field === columnField);
×
UNCOV
1214
        if (row && row instanceof IgxGridRow && !row.data?.detailsData && column) {
×
UNCOV
1215
            if (this.pagingMode === 'remote' && this.page !== 0) {
×
1216
                row.index = rowIndex + this.perPage * this.page;
×
1217
            }
UNCOV
1218
            return new IgxGridCell(this, row.index, column);
×
1219
        }
1220
    }
1221

1222
    /**
1223
     * Returns a `CellType` object that matches the conditions.
1224
     *
1225
     * @remarks
1226
     * Requires that the primaryKey property is set.
1227
     * @example
1228
     * ```typescript
1229
     * grid.getCellByKey(1, 'index');
1230
     * ```
1231
     * @param rowSelector match any rowID
1232
     * @param columnField
1233
     */
1234
    public getCellByKey(rowSelector: any, columnField: string): CellType {
UNCOV
1235
        const row = this.getRowByKey(rowSelector);
×
UNCOV
1236
        const column = this._columns.find((col) => col.field === columnField);
×
UNCOV
1237
        if (row && column) {
×
UNCOV
1238
            return new IgxGridCell(this, row.index, column);
×
1239
        }
1240
    }
1241

1242
    public override pinRow(rowID: any, index?: number): boolean {
UNCOV
1243
        const row = this.getRowByKey(rowID);
×
UNCOV
1244
        return super.pinRow(rowID, index, row);
×
1245
    }
1246

1247
    public override unpinRow(rowID: any): boolean {
UNCOV
1248
        const row = this.getRowByKey(rowID);
×
UNCOV
1249
        return super.unpinRow(rowID, row);
×
1250
    }
1251

1252
    /**
1253
     * @hidden @internal
1254
     */
1255
    public createRow(index: number, data?: any): RowType {
1256
        let row: RowType;
1257

UNCOV
1258
        const dataIndex = this._getDataViewIndex(index);
×
UNCOV
1259
        const rec = data ?? this.dataView[dataIndex];
×
1260

UNCOV
1261
        if (rec && this.isGroupByRecord(rec)) {
×
UNCOV
1262
            row = new IgxGroupByRow(this, index, rec);
×
1263
        }
UNCOV
1264
        if (rec && this.isSummaryRow(rec)) {
×
UNCOV
1265
            row = new IgxSummaryRow(this, index, rec.summaries);
×
1266
        }
1267
        // if found record is a no a groupby or summary row, return IgxGridRow instance
UNCOV
1268
        if (!row && rec) {
×
UNCOV
1269
            row = new IgxGridRow(this, index, rec);
×
1270
        }
1271

UNCOV
1272
        return row;
×
1273
    }
1274

1275
    /**
1276
     * @hidden @internal
1277
     */
1278
    protected override get defaultTargetBodyHeight(): number {
UNCOV
1279
        const allItems = this.totalItemCount || this.dataLength;
×
UNCOV
1280
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
×
1281
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
×
1282
    }
1283

1284
    /**
1285
     * @hidden @internal
1286
     */
1287
    protected override getGroupAreaHeight(): number {
UNCOV
1288
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
×
1289
    }
1290

1291
    /**
1292
     * @hidden @internal
1293
     */
1294
    protected override onColumnsAddedOrRemoved() {
1295
        // update grouping states
UNCOV
1296
        this.groupablePipeTrigger++;
×
UNCOV
1297
        if (this.groupingExpressions && this.hideGroupedColumns) {
×
UNCOV
1298
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1299
        }
UNCOV
1300
        super.onColumnsAddedOrRemoved();
×
1301
    }
1302

1303
    /**
1304
     * @hidden @internal
1305
     */
1306
    protected override scrollTo(row: any | number, column: any | number): void {
UNCOV
1307
        if (this.groupingExpressions && this.groupingExpressions.length
×
1308
            && typeof (row) !== 'number') {
UNCOV
1309
            const rowIndex = this.groupingResult.indexOf(row);
×
UNCOV
1310
            const groupByRecord = this.groupingMetadata[rowIndex];
×
UNCOV
1311
            if (groupByRecord) {
×
UNCOV
1312
                this._fullyExpandGroup(groupByRecord);
×
1313
            }
1314
        }
1315

UNCOV
1316
        super.scrollTo(row, column, this.groupingFlatResult);
×
1317
    }
1318

1319
    /**
1320
     * @hidden @internal
1321
     */
1322
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
UNCOV
1323
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
×
1324
    }
1325

1326
    /**
1327
     * @hidden
1328
     */
1329
    protected _toggleGroup(groupRow: IGroupByRecord) {
UNCOV
1330
        this._gridAPI.groupBy_toggle_group(groupRow);
×
1331
    }
1332

1333
    /**
1334
     * @hidden @internal
1335
     */
1336
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
UNCOV
1337
        this._gridAPI.groupBy_fully_expand_group(groupRow);
×
1338
    }
1339

1340
    /**
1341
     * @hidden @internal
1342
     */
1343
    protected _applyGrouping() {
UNCOV
1344
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
×
1345
    }
1346

1347
    private _setupNavigationService() {
UNCOV
1348
        if (this.hasColumnLayouts) {
×
UNCOV
1349
            this.navigation = new IgxGridMRLNavigationService(this.platform);
×
UNCOV
1350
            this.navigation.grid = this;
×
1351
        }
1352
    }
1353

1354
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
UNCOV
1355
        if (expression instanceof Array) {
×
UNCOV
1356
            for (const singleExpression of expression) {
×
UNCOV
1357
                if (!singleExpression.fieldName) {
×
UNCOV
1358
                    return true;
×
1359
                }
1360
            }
UNCOV
1361
            return false;
×
1362
        }
UNCOV
1363
        return !expression.fieldName;
×
1364
    }
1365

1366
    private _setGroupColsVisibility(value) {
UNCOV
1367
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
×
UNCOV
1368
            this.groupingExpressions.forEach((expr) => {
×
UNCOV
1369
                const col = this.getColumnByName(expr.fieldName);
×
UNCOV
1370
                col.hidden = value;
×
1371
            });
1372
        }
1373
    }
1374

1375
    private stringifyCallback(key: string, val: any) {
1376
        // Workaround for Blazor, since its wrappers inject this externalObject that cannot serialize.
UNCOV
1377
        if (key === 'externalObject') {
×
1378
            return undefined;
×
1379
        }
UNCOV
1380
        return val;
×
1381
    }
1382
}
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