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

IgniteUI / igniteui-angular / 18686761775

21 Oct 2025 02:12PM UTC coverage: 91.6% (+0.06%) from 91.539%
18686761775

push

github

web-flow
feat(timepicker): update timepicker sample with properties panel (#16338)

13841 of 16237 branches covered (85.24%)

27851 of 30405 relevant lines covered (91.6%)

34630.33 hits per line

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

96.5
/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, IgxGridCellMergePipe, IgxGridUnmergeActivePipe } 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
import { IgxScrollInertiaDirective } from '../../directives/scroll-inertia/scroll_inertia.directive';
58

59
let NEXT_ID = 0;
3✔
60

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

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

174
    /**
175
     * Emitted when grouping is performed.
176
     *
177
     * @example
178
     * ```html
179
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpressionsChange)="groupingExpressionsChange($event)"></igx-grid>
180
     * ```
181
     */
182
    @Output()
183
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
2,066✔
184

185
    /**
186
     * Emitted when groups are expanded/collapsed.
187
     *
188
     * @example
189
     * ```html
190
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpansionStateChange)="groupingExpansionStateChange($event)"></igx-grid>
191
     * ```
192
     */
193
    @Output()
194
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
2,066✔
195

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

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

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

245
    /**
246
     * @hidden @internal
247
     */
248
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
249
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
250

251

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

286
    /**
287
     * @hidden @internal
288
     */
289
    @HostBinding('attr.role')
290
    public role = 'grid';
2,066✔
291

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

306
    /**
307
     * @hidden @internal
308
     */
309
    @ViewChild('record_template', { read: TemplateRef, static: true })
310
    protected recordTemplate: TemplateRef<any>;
311

312
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
313
    protected detailTemplateContainer: TemplateRef<any>;
314

315
    @ViewChild('group_template', { read: TemplateRef, static: true })
316
    protected defaultGroupTemplate: TemplateRef<any>;
317

318
    @ViewChild('summary_template', { read: TemplateRef, static: true })
319
    protected summaryTemplate: TemplateRef<any>;
320

321
    /**
322
     * @hidden @internal
323
     */
324
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
325
    protected groupTemplate: IgxGroupByRowTemplateDirective;
326

327
    /**
328
     * @hidden
329
     * @internal
330
     */
331
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
332
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
333

334
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
335
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
336

337
    private _groupsRecords: IGroupByRecord[] = [];
2,066✔
338
    /**
339
     * Gets the hierarchical representation of the group by records.
340
     *
341
     * @example
342
     * ```typescript
343
     * let groupRecords = this.grid.groupsRecords;
344
     * ```
345
     */
346
    public get groupsRecords(): IGroupByRecord[] {
347
        return this._groupsRecords;
26,120✔
348
    }
349

350
    /**
351
     * @hidden @internal
352
     * Includes children of collapsed group rows.
353
     */
354
    public groupingResult: any[];
355

356
    /**
357
     * @hidden @internal
358
     */
359
    public groupingMetadata: any[];
360

361
    /**
362
     * @hidden @internal
363
     * Does not include children of collapsed group rows.
364
     */
365
    public groupingFlatResult: any[];
366
    /**
367
     * @hidden
368
     */
369
    protected _groupingExpressions: IGroupingExpression[] = [];
2,066✔
370
    /**
371
     * @hidden
372
     */
373
    protected _groupingExpandState: IGroupByExpandState[] = [];
2,066✔
374
    /**
375
     * @hidden
376
     */
377
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
378

379
    /**
380
     * @hidden
381
     */
382
    protected _groupStrategy: IGridGroupingStrategy;
383
    /**
384
     * @hidden
385
     */
386
    protected groupingDiffer;
387
    private _data?: any[] | null;
388
    private _hideGroupedColumns = false;
2,066✔
389
    private _dropAreaMessage = null;
2,066✔
390
    private _showGroupArea = true;
2,066✔
391

392
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
393
    private _detailTemplate;
394

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

409
    public set data(value: any[] | null) {
410
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
2,153✔
411
        const oldData = this._data;
2,153✔
412
        this._data = value || [];
2,153✔
413
        this.summaryService.clearSummaryCache();
2,153✔
414
        if (!this._init) {
2,153✔
415
            this.validation.updateAll(this._data);
103✔
416
        }
417

418
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
2,153✔
419
            this.setupColumns();
20✔
420
        }
421

422
        this.cdr.markForCheck();
2,153✔
423
        if (this.isPercentHeight) {
2,153✔
424
            this.notifyChanges(true);
978✔
425
        }
426
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
427
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
2,153✔
428
            this.recalculateAutoSizes();
1✔
429
        }
430
        this.checkPrimaryKeyField();
2,153✔
431
    }
432

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

449
    public get totalItemCount() {
450
        return this.verticalScrollContainer.totalItemCount;
1,251✔
451
    }
452

453
    private get _gridAPI(): IgxGridAPIService {
454
        return this.gridAPI as IgxGridAPIService;
14,406✔
455
    }
456

457
    private childDetailTemplates: Map<any, any> = new Map();
2,066✔
458

459
    /**
460
     * @hidden @internal
461
     */
462
    public groupingPerformedSubject = new Subject<void>();
2,066✔
463

464
    /**
465
     * @hidden @internal
466
     */
467
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
2,066✔
468

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

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

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

546
    public set groupingExpansionState(value) {
547
        if (value !== this._groupingExpandState) {
95✔
548
            this.groupingExpansionStateChange.emit(value);
95✔
549
        }
550
        this._groupingExpandState = value;
95✔
551
        if (this.gridAPI.grid) {
95✔
552
            this.cdr.detectChanges();
95✔
553
        }
554
    }
555

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

571
    public set hideGroupedColumns(value: boolean) {
572
        if (value) {
36✔
573
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
16✔
574
        } else {
575
            this.groupingDiffer = null;
20✔
576
        }
577
        if (this._columns && this.groupingExpressions) {
36✔
578
            this._setGroupColsVisibility(value);
36✔
579
        }
580

581
        this._hideGroupedColumns = value;
36✔
582
    }
583

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

599
    public set groupStrategy(value: IGridGroupingStrategy) {
600
        this._groupStrategy = value;
14✔
601
    }
602

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

621
    public get dropAreaMessage(): string {
622
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
3,836✔
623
    }
624

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

639
    /**
640
     * Gets the group by row selector template.
641
     */
642
    @Input()
643
    public get groupByRowSelectorTemplate(): TemplateRef<IgxGroupByRowSelectorTemplateContext> {
644
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
389✔
645
    }
646

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

664
    /**
665
     * @hidden @internal
666
     */
667
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
668
        return {
2,378✔
669
            $implicit: rowData,
670
            index
671
        };
672
    }
673

674
    /**
675
     * @hidden @internal
676
     */
677
    public detailsViewFocused(container, rowIndex) {
678
        this.navigation.setActiveNode({ row: rowIndex });
2✔
679
    }
680

681
    /**
682
     * @hidden @internal
683
     */
684
    public override get hasDetails() {
685
        return !!this.detailTemplate;
312,279✔
686
    }
687

688
    /**
689
     * @hidden @internal
690
     */
691
    public getRowTemplate(rowData) {
692
        if (this.isGroupByRecord(rowData)) {
153,017✔
693
            return this.defaultGroupTemplate;
9,033✔
694
        } else if (this.isSummaryRow(rowData)) {
143,984✔
695
            return this.summaryTemplate;
1,457✔
696
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
142,527✔
697
            return this.detailTemplateContainer;
2,378✔
698
        } else {
699
            return this.recordTemplate;
140,149✔
700
        }
701
    }
702

703
    /**
704
     * @hidden @internal
705
     */
706
    public override isDetailRecord(record) {
707
        return record && record.detailsData !== undefined;
168,034✔
708
    }
709

710
    /**
711
     * @hidden @internal
712
     */
713
    public isDetailActive(rowIndex) {
714
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
2,378!
715
    }
716

717
    /**
718
     * Gets/Sets the template reference for the group row.
719
     *
720
     * @example
721
     * ```
722
     * const groupRowTemplate = this.grid.groupRowTemplate;
723
     * this.grid.groupRowTemplate = myRowTemplate;
724
     * ```
725
     */
726
    @Input()
727
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
728
        return this._groupRowTemplate;
2,237✔
729
    }
730

731
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
732
        this._groupRowTemplate = template;
1✔
733
        this.notifyChanges();
1✔
734
    }
735

736
    /** @hidden @internal */
737
    public trackChanges: (index, rec) => any;
738

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

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

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

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

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

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

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

864
    /**
865
     * @hidden @internal
866
     */
867
    public override isGroupByRecord(record: any): boolean {
868
        // return record.records instance of GroupedRecords fails under Webpack
869
        return record && record?.records && record.records?.length &&
370,195✔
870
            record.expression && record.expression?.fieldName;
871
    }
872

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

887
    /** @hidden @internal */
888
    public get hasGroupableColumns(): boolean {
889
        return this._columns.some((col) => col.groupable && !col.columnGroup);
161,590✔
890
    }
891

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

914
    /**
915
     * @hidden @internal
916
     */
917
    public override isColumnGrouped(fieldName: string): boolean {
918
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
2!
919
    }
920

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

965
    /**
966
     * @hidden @internal
967
     */
968
    public viewCreatedHandler(args) {
969
        if (args.context.templateID.type === 'detailRow') {
19,735✔
970
            this.childDetailTemplates.set(args.context.$implicit, args);
203✔
971
        }
972
    }
973

974
    /**
975
     * @hidden @internal
976
     */
977
    public viewMovedHandler(args) {
978
        if (args.context.templateID.type === 'detailRow') {
64✔
979
            // view was moved, update owner in cache
980
            const key = args.context.$implicit;
64✔
981
            const cachedData = this.childDetailTemplates.get(key);
64✔
982
            cachedData.owner = args.owner;
64✔
983
        }
984
    }
985

986
    /**
987
     * @hidden @internal
988
     */
989
    public get iconTemplate() {
990
        if (this.groupsExpanded) {
2,140✔
991
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
2,082✔
992
        } else {
993
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
58✔
994
        }
995
    }
996

997
    /**
998
     * @hidden @internal
999
     */
1000
    public override ngAfterContentInit() {
1001
        super.ngAfterContentInit();
2,066✔
1002
        if (this.allowFiltering && this.hasColumnLayouts) {
2,066✔
1003
            this.filterMode = FilterMode.excelStyleFilter;
2✔
1004
        }
1005
        if (this.groupTemplate) {
2,066✔
1006
            this._groupRowTemplate = this.groupTemplate.template;
2✔
1007
        }
1008

1009
        if (this.detailTemplateDirective) {
2,066✔
1010
            this._detailTemplate = this.detailTemplateDirective;
61✔
1011
        }
1012

1013

1014
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
2,066✔
1015
            this._setGroupColsVisibility(this.hideGroupedColumns);
11✔
1016
        }
1017
        this._setupNavigationService();
2,066✔
1018
    }
1019

1020
    /**
1021
     * @hidden @internal
1022
     */
1023
    public override ngAfterViewInit() {
1024
        super.ngAfterViewInit();
2,080✔
1025
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
2,080✔
1026
            const rowData = view.context.$implicit;
2,771✔
1027
            if (this.isDetailRecord(rowData)) {
2,771✔
1028
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
6✔
1029
                if (cachedData) {
6✔
1030
                    const tmlpOutlet = cachedData.owner;
6✔
1031
                    tmlpOutlet._viewContainerRef.detach(0);
6✔
1032
                }
1033
            }
1034
        });
1035

1036
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
2,080✔
1037
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
380✔
1038
                return;
141✔
1039
            }
1040

1041
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
239✔
1042
                const fieldName = sortExpr.fieldName;
13✔
1043
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
15✔
1044
                if (groupingExpr) {
13✔
1045
                    groupingExpr.dir = sortExpr.dir;
7✔
1046
                }
1047
            });
1048
        });
1049
    }
1050

1051
    /**
1052
     * @hidden @internal
1053
     */
1054
    public override ngOnInit() {
1055
        super.ngOnInit();
2,066✔
1056
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
2,443,444✔
1057
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
2,066✔
1058
            this.crudService.endEdit(false);
266✔
1059
            this.summaryService.updateSummaryCache(args);
266✔
1060
            this._headerFeaturesWidth = NaN;
266✔
1061
        });
1062
    }
1063

1064
    /**
1065
     * @hidden @internal
1066
     */
1067
    public override ngDoCheck(): void {
1068
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
8,879✔
1069
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
37✔
1070
            if (changes && this._columns.length > 0) {
37✔
1071
                changes.forEachAddedItem((rec) => {
14✔
1072
                    const col = this.getColumnByName(rec.item.fieldName);
21✔
1073
                    if (col) {
21✔
1074
                        col.hidden = true;
20✔
1075
                    }
1076
                });
1077
                changes.forEachRemovedItem((rec) => {
14✔
1078
                    const col = this.getColumnByName(rec.item.fieldName);
×
1079
                    col.hidden = false;
×
1080
                });
1081
            }
1082
        }
1083
        super.ngDoCheck();
8,879✔
1084
    }
1085

1086
    /**
1087
     * @hidden @internal
1088
     */
1089
    public dataLoading(event) {
1090
        this.dataPreLoad.emit(event);
135✔
1091
    }
1092

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

1105
            const process = (record) => {
15✔
1106
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
220✔
1107
                    source.push(null);
97✔
1108
                    return;
97✔
1109
                }
1110
                source.push(record);
123✔
1111

1112
            };
1113

1114
            this.dataView.forEach(process);
15✔
1115
            return this.extractDataFromSelection(source, formatters, headers);
15✔
1116
        } else {
1117
            return super.getSelectedData(formatters, headers);
167✔
1118
        }
1119
    }
1120

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

1143
        if (this.pagingMode === 'remote' && this.page !== 0) {
597✔
1144
            row.index = index + this.perPage * this.page;
2✔
1145
        }
1146
        return row;
597✔
1147
    }
1148

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

1169
        return new IgxGridRow(this, index, rec);
156✔
1170
    }
1171

1172
    /**
1173
     * @hidden @internal
1174
     */
1175
    public allRows(): RowType[] {
1176
        return this.dataView.map((rec, index) => {
78✔
1177
            this.pagingMode === 'remote' && this.page !== 0 ?
3,909!
1178
                index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
1179
            return this.createRow(index);
3,909✔
1180
        });
1181
    }
1182

1183
    /**
1184
     * Returns the collection of `IgxGridRow`s for current page.
1185
     *
1186
     * @hidden @internal
1187
     */
1188
    public dataRows(): RowType[] {
1189
        return this.allRows().filter(row => row instanceof IgxGridRow);
3,909✔
1190
    }
1191

1192
    /**
1193
     * Returns an array of the selected `IgxGridCell`s.
1194
     *
1195
     * @example
1196
     * ```typescript
1197
     * const selectedCells = this.grid.selectedCells;
1198
     * ```
1199
     */
1200
    public get selectedCells(): CellType[] {
1201
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
34,455✔
1202
            .reduce((a, b) => a.concat(b), []);
2,213✔
1203
    }
1204

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

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

1246
    public override pinRow(rowID: any, index?: number): boolean {
1247
        const row = this.getRowByKey(rowID);
94✔
1248
        return super.pinRow(rowID, index, row);
94✔
1249
    }
1250

1251
    public override unpinRow(rowID: any): boolean {
1252
        const row = this.getRowByKey(rowID);
18✔
1253
        return super.unpinRow(rowID, row);
18✔
1254
    }
1255

1256
    /**
1257
     * @hidden @internal
1258
     */
1259
    public createRow(index: number, data?: any): RowType {
1260
        let row: RowType;
1261

1262
        const dataIndex = this._getDataViewIndex(index);
46,714✔
1263
        const rec = data ?? this.dataView[dataIndex];
46,714✔
1264

1265
        if (rec && this.isGroupByRecord(rec)) {
46,714✔
1266
            row = new IgxGroupByRow(this, index, rec);
127✔
1267
        }
1268
        if (rec && this.isSummaryRow(rec)) {
46,714✔
1269
            row = new IgxSummaryRow(this, index, rec.summaries);
20✔
1270
        }
1271
        // if found record is a no a groupby or summary row, return IgxGridRow instance
1272
        if (!row && rec) {
46,714✔
1273
            row = new IgxGridRow(this, index, rec);
44,994✔
1274
        }
1275

1276
        return row;
46,714✔
1277
    }
1278

1279
    /**
1280
     * @hidden @internal
1281
     */
1282
    protected override get defaultTargetBodyHeight(): number {
1283
        const allItems = this.totalItemCount || this.dataLength;
178✔
1284
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
178✔
1285
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
178✔
1286
    }
1287

1288
    /**
1289
     * @hidden @internal
1290
     */
1291
    protected override getGroupAreaHeight(): number {
1292
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
5,492✔
1293
    }
1294

1295
    /**
1296
     * @hidden @internal
1297
     */
1298
    protected override onColumnsAddedOrRemoved() {
1299
        // update grouping states
1300
        this.groupablePipeTrigger++;
64✔
1301
        if (this.groupingExpressions && this.hideGroupedColumns) {
64✔
1302
            this._setGroupColsVisibility(this.hideGroupedColumns);
2✔
1303
        }
1304
        super.onColumnsAddedOrRemoved();
64✔
1305
    }
1306

1307
    /**
1308
     * @hidden @internal
1309
     */
1310
    protected override scrollTo(row: any | number, column: any | number): void {
1311
        if (this.groupingExpressions && this.groupingExpressions.length
108✔
1312
            && typeof (row) !== 'number') {
1313
            const rowIndex = this.groupingResult.indexOf(row);
31✔
1314
            const groupByRecord = this.groupingMetadata[rowIndex];
31✔
1315
            if (groupByRecord) {
31✔
1316
                this._fullyExpandGroup(groupByRecord);
31✔
1317
            }
1318
        }
1319

1320
        super.scrollTo(row, column, this.groupingFlatResult);
108✔
1321
    }
1322

1323
    /**
1324
     * @hidden @internal
1325
     */
1326
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
1327
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
13,150✔
1328
    }
1329

1330
    /**
1331
     * @hidden
1332
     */
1333
    protected _toggleGroup(groupRow: IGroupByRecord) {
1334
        this._gridAPI.groupBy_toggle_group(groupRow);
58✔
1335
    }
1336

1337
    /**
1338
     * @hidden @internal
1339
     */
1340
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
1341
        this._gridAPI.groupBy_fully_expand_group(groupRow);
31✔
1342
    }
1343

1344
    /**
1345
     * @hidden @internal
1346
     */
1347
    protected _applyGrouping() {
1348
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
406✔
1349
    }
1350

1351
    protected _setupNavigationService() {
1352
        if (this.hasColumnLayouts) {
2,066✔
1353
            this.navigation = new IgxGridMRLNavigationService(this.platform);
121✔
1354
            this.navigation.grid = this;
121✔
1355
        }
1356
    }
1357

1358
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
1359
        if (expression instanceof Array) {
234✔
1360
            for (const singleExpression of expression) {
21✔
1361
                if (!singleExpression.fieldName) {
54✔
1362
                    return true;
1✔
1363
                }
1364
            }
1365
            return false;
20✔
1366
        }
1367
        return !expression.fieldName;
213✔
1368
    }
1369

1370
    private _setGroupColsVisibility(value) {
1371
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
49✔
1372
            this.groupingExpressions.forEach((expr) => {
17✔
1373
                const col = this.getColumnByName(expr.fieldName);
6✔
1374
                col.hidden = value;
6✔
1375
            });
1376
        }
1377
    }
1378

1379
    private stringifyCallback(key: string, val: any) {
1380
        // Workaround for Blazor, since its wrappers inject this externalObject that cannot serialize.
1381
        if (key === 'externalObject') {
2,625!
1382
            return undefined;
×
1383
        }
1384
        return val;
2,625✔
1385
    }
1386
}
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