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

IgniteUI / igniteui-angular / 23353730325

20 Mar 2026 05:03PM UTC coverage: 9.784% (-79.5%) from 89.264%
23353730325

Pull #17069

github

web-flow
Merge cfa7e86d1 into a4dc50177
Pull Request #17069: fix(IgxGrid): Do not apply width constraint to groups.

921 of 16963 branches covered (5.43%)

Branch coverage included in aggregate %.

1 of 3 new or added lines in 1 file covered. (33.33%)

25213 existing lines in 340 files now uncovered.

3842 of 31721 relevant lines covered (12.11%)

6.13 hits per line

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

18.91
/projects/igniteui-angular/grids/grid/src/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
import {
8
    CellType,
9
    FilterMode,
10
    GridType,
11
    IGX_GRID_BASE,
12
    IGX_GRID_SERVICE_BASE,
13
    IgxColumnComponent,
14
    IgxColumnMovingDropDirective,
15
    IgxColumnResizingService,
16
    IgxFilteringService,
17
    IgxGridAddRowPipe,
18
    IgxGridBodyDirective,
19
    IgxGridCell,
20
    IgxGridColumnResizerComponent,
21
    IgxGridCRUDService,
22
    IgxGridDetailTemplateDirective,
23
    IgxGridDragSelectDirective,
24
    IgxGridHeaderRowComponent,
25
    IgxGridMasterDetailContext,
26
    IgxGridMRLNavigationService,
27
    IgxGridNavigationService,
28
    IgxGridRow,
29
    IgxGridRowClassesPipe,
30
    IgxGridRowPinningPipe,
31
    IgxGridRowStylesPipe,
32
    IgxGridSelectionService,
33
    IgxGridSummaryService,
34
    IgxGridTransactionPipe,
35
    IgxGridValidationService,
36
    IgxGroupByRow,
37
    IgxGroupByRowSelectorDirective,
38
    IgxGroupByRowSelectorTemplateContext,
39
    IgxGroupByRowTemplateContext,
40
    IgxGroupByRowTemplateDirective,
41
    IgxHasVisibleColumnsPipe,
42
    IgxRowEditTabStopDirective,
43
    IgxStringReplacePipe,
44
    IgxSummaryDataPipe,
45
    IgxSummaryRow,
46
    IgxSummaryRowComponent,
47
    RowType
48
} from 'igniteui-angular/grids/core';
49
import { IgxGridAPIService } from './grid-api.service';
50
import { IgxGridGroupByRowComponent } from './groupby-row.component';
51
import { IgxGridGroupByAreaComponent } from './grouping/grid-group-by-area.component';
52
import { take, takeUntil } from 'rxjs/operators';
53
import { cloneArray, IBaseEventArgs, IGridGroupingStrategy, IGroupByExpandState, IGroupByRecord, IGroupingExpression, IgxOverlayOutletDirective, ISortingExpression } from 'igniteui-angular/core';
54
import { IgxGridDetailsPipe } from './grid.details.pipe';
55
import { IgxGridSummaryPipe } from './grid.summary.pipe';
56
import { IgxGridGroupingPipe, IgxGridPagingPipe, IgxGridSortingPipe, IgxGridFilteringPipe, IgxGridCellMergePipe, IgxGridUnmergeActivePipe } from './grid.pipes';
57
import { IgxGridRowComponent } from './grid-row.component';
58
import { Observable, Subject } from 'rxjs';
59
import { IForOfState, IgxButtonDirective, IgxForOfScrollSyncService, IgxForOfSyncService, IgxGridForOfDirective, IgxRippleDirective, IgxScrollInertiaDirective, IgxTemplateOutletDirective, IgxToggleDirective } from 'igniteui-angular/directives';
60
import { IgxCircularProgressBarComponent } from 'igniteui-angular/progressbar';
61
import { IgxSnackbarComponent } from 'igniteui-angular/snackbar';
62
import { IgxIconComponent } from 'igniteui-angular/icon';
63
import { IgxGridBaseDirective } from './grid-base.directive';
64

65
let NEXT_ID = 0;
3✔
66

67
export interface IGroupingDoneEventArgs extends IBaseEventArgs {
68
    expressions: Array<ISortingExpression> | ISortingExpression;
69
    groupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
70
    ungroupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
71
}
72

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

181
    /**
182
     * Emitted when grouping is performed.
183
     *
184
     * @example
185
     * ```html
186
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpressionsChange)="groupingExpressionsChange($event)"></igx-grid>
187
     * ```
188
     */
189
    @Output()
190
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
2✔
191

192
    /**
193
     * Emitted when groups are expanded/collapsed.
194
     *
195
     * @example
196
     * ```html
197
     * <igx-grid #grid [data]="localData" [autoGenerate]="true" (groupingExpansionStateChange)="groupingExpansionStateChange($event)"></igx-grid>
198
     * ```
199
     */
200
    @Output()
201
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
2✔
202

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

222
    /**
223
     * Gets/Sets whether created groups are rendered expanded or collapsed.
224
     *
225
     * @remarks
226
     * The default rendered state is expanded.
227
     * @example
228
     * ```html
229
     * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid>
230
     * ```
231
     */
232
    @Input({ transform: booleanAttribute })
233
    public groupsExpanded = true;
2✔
234

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

252
    /**
253
     * @hidden @internal
254
     */
255
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
256
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
257

258

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

293
    /**
294
     * @hidden @internal
295
     */
296
    @HostBinding('attr.role')
297
    public role = 'grid';
2✔
298

299
    /**
300
     * Gets/Sets the value of the `id` attribute.
301
     *
302
     * @remarks
303
     * If not provided it will be automatically generated.
304
     * @example
305
     * ```html
306
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid>
307
     * ```
308
     */
309
    @HostBinding('attr.id')
310
    @Input()
311
    public id = `igx-grid-${NEXT_ID++}`;
2✔
312

313
    /**
314
     * @hidden @internal
315
     */
316
    @ViewChild('record_template', { read: TemplateRef, static: true })
317
    protected recordTemplate: TemplateRef<any>;
318

319
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
320
    protected detailTemplateContainer: TemplateRef<any>;
321

322
    @ViewChild('group_template', { read: TemplateRef, static: true })
323
    protected defaultGroupTemplate: TemplateRef<any>;
324

325
    @ViewChild('summary_template', { read: TemplateRef, static: true })
326
    protected summaryTemplate: TemplateRef<any>;
327

328
    /**
329
     * @hidden @internal
330
     */
331
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
332
    protected groupTemplate: IgxGroupByRowTemplateDirective;
333

334
    /**
335
     * @hidden
336
     * @internal
337
     */
338
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
339
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
340

341
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
342
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
343

344
    private _groupsRecords: IGroupByRecord[] = [];
2✔
345
    /**
346
     * Gets the hierarchical representation of the group by records.
347
     *
348
     * @example
349
     * ```typescript
350
     * let groupRecords = this.grid.groupsRecords;
351
     * ```
352
     */
353
    public get groupsRecords(): IGroupByRecord[] {
354
        return this._groupsRecords;
10✔
355
    }
356

357
    /**
358
     * @hidden @internal
359
     * Includes children of collapsed group rows.
360
     */
361
    public groupingResult: any[];
362

363
    /**
364
     * @hidden @internal
365
     */
366
    public groupingMetadata: any[];
367

368
    /**
369
     * @hidden @internal
370
     * Does not include children of collapsed group rows.
371
     */
372
    public groupingFlatResult: any[];
373
    /**
374
     * @hidden
375
     */
376
    protected _groupingExpressions: IGroupingExpression[] = [];
2✔
377
    /**
378
     * @hidden
379
     */
380
    protected _groupingExpandState: IGroupByExpandState[] = [];
2✔
381
    /**
382
     * @hidden
383
     */
384
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
385

386
    /**
387
     * @hidden
388
     */
389
    protected _groupStrategy: IGridGroupingStrategy;
390
    /**
391
     * @hidden
392
     */
393
    protected groupingDiffer;
394
    private _data?: any[] | null;
395
    private _hideGroupedColumns = false;
2✔
396
    private _dropAreaMessage = null;
2✔
397
    private _showGroupArea = true;
2✔
398

399
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
400
    private _detailTemplate;
401

402
    /**
403
     * Gets/Sets the array of data that populates the component.
404
     *
405
     * @example
406
     * ```html
407
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
408
     * ```
409
     */
410
    /* treatAsRef */
411
    @Input()
412
    public get data(): any[] | null {
413
        return this._data;
40✔
414
    }
415

416
    public set data(value: any[] | null) {
417
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
2!
418
        const oldData = this._data;
2✔
419
        this._data = value || [];
2!
420
        this.summaryService.clearSummaryCache();
2✔
421
        if (!this._init) {
2!
UNCOV
422
            this.validation.updateAll(this._data);
×
423
        }
424

425
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data) && this.gridAPI.grid) {
2!
UNCOV
426
            this.setupColumns();
×
427
        }
428

429
        this.cdr.markForCheck();
2✔
430
        if (this.isPercentHeight) {
2!
UNCOV
431
            this.notifyChanges(true);
×
432
        }
433
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
434
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
2!
UNCOV
435
            this.recalculateAutoSizes();
×
436
        }
437
        this.checkPrimaryKeyField();
2✔
438
    }
439

440
    /**
441
     * Gets/Sets the total number of records in the data source.
442
     *
443
     * @remarks
444
     * This property is required for remote grid virtualization to function when it is bound to remote data.
445
     * @example
446
     * ```typescript
447
     * const itemCount = this.grid1.totalItemCount;
448
     * this.grid1.totalItemCount = 55;
449
     * ```
450
     */
451
    @Input()
452
    public set totalItemCount(count) {
UNCOV
453
        this.verticalScrollContainer.totalItemCount = count;
×
454
    }
455

456
    public get totalItemCount() {
UNCOV
457
        return this.verticalScrollContainer.totalItemCount;
×
458
    }
459

460
    private get _gridAPI(): IgxGridAPIService {
UNCOV
461
        return this.gridAPI as IgxGridAPIService;
×
462
    }
463

464
    private childDetailTemplates: Map<any, any> = new Map();
2✔
465

466
    /**
467
     * @hidden @internal
468
     */
469
    public groupingPerformedSubject = new Subject<void>();
2✔
470

471
    /**
472
     * @hidden @internal
473
     */
474
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
2✔
475

476
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
477
    /**
478
     * Gets/Sets the group by state.
479
     *
480
     * @example
481
     * ```typescript
482
     * let groupByState = this.grid.groupingExpressions;
483
     * this.grid.groupingExpressions = [...];
484
     * ```
485
     * @remarks
486
     * Supports two-way data binding.
487
     * @example
488
     * ```html
489
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid>
490
     * ```
491
     */
492
    @Input()
493
    public get groupingExpressions(): IGroupingExpression[] {
494
        return this._groupingExpressions;
498✔
495
    }
496

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

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

553
    public set groupingExpansionState(value) {
UNCOV
554
        if (value !== this._groupingExpandState) {
×
UNCOV
555
            this.groupingExpansionStateChange.emit(value);
×
556
        }
UNCOV
557
        this._groupingExpandState = value;
×
UNCOV
558
        if (this.gridAPI.grid) {
×
UNCOV
559
            this.cdr.detectChanges();
×
560
        }
561
    }
562

563
    /**
564
     * Gets/Sets whether the grouped columns should be hidden.
565
     *
566
     * @remarks
567
     * The default value is "false"
568
     * @example
569
     * ```html
570
     * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid>
571
     * ```
572
     */
573
    @Input({ transform: booleanAttribute })
574
    public get hideGroupedColumns() {
575
        return this._hideGroupedColumns;
2✔
576
    }
577

578
    public set hideGroupedColumns(value: boolean) {
UNCOV
579
        if (value) {
×
UNCOV
580
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
×
581
        } else {
UNCOV
582
            this.groupingDiffer = null;
×
583
        }
UNCOV
584
        if (this._columns && this.groupingExpressions) {
×
UNCOV
585
            this._setGroupColsVisibility(value);
×
586
        }
587

UNCOV
588
        this._hideGroupedColumns = value;
×
589
    }
590

591
    /**
592
     * Gets/Sets the grouping strategy of the grid.
593
     *
594
     * @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
595
     *
596
     * @example
597
     * ```html
598
     *  <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
599
     * ```
600
     */
601
    @Input()
602
    public get groupStrategy(): IGridGroupingStrategy {
603
        return this._groupStrategy;
10✔
604
    }
605

606
    public set groupStrategy(value: IGridGroupingStrategy) {
UNCOV
607
        this._groupStrategy = value;
×
608
    }
609

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

628
    public get dropAreaMessage(): string {
UNCOV
629
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
×
630
    }
631

632
    /**
633
     * @hidden @internal
634
     */
635
    public get groupsRowList() {
UNCOV
636
        const res = new QueryList<any>();
×
UNCOV
637
        if (!this._groupsRowList) {
×
638
            return res;
×
639
        }
UNCOV
640
        const rList = this._groupsRowList.filter(item => item.element.nativeElement.parentElement !== null)
×
UNCOV
641
            .sort((item1, item2) => item1.index - item2.index);
×
UNCOV
642
        res.reset(rList);
×
UNCOV
643
        return res;
×
644
    }
645

646
    /**
647
     * Gets the group by row selector template.
648
     */
649
    @Input()
650
    public get groupByRowSelectorTemplate(): TemplateRef<IgxGroupByRowSelectorTemplateContext> {
UNCOV
651
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
×
652
    }
653

654
    /**
655
     * Sets the group by row selector template.
656
     * ```html
657
     * <ng-template #template igxGroupByRowSelector let-groupByRowContext>
658
     * {{ groupByRowContext.selectedCount }} / {{ groupByRowContext.totalCount  }}
659
     * </ng-template>
660
     * ```
661
     * ```typescript
662
     * @ViewChild("'template'", {read: TemplateRef })
663
     * public template: TemplateRef<any>;
664
     * this.grid.groupByRowSelectorTemplate = this.template;
665
     * ```
666
     */
667
    public set groupByRowSelectorTemplate(template: TemplateRef<IgxGroupByRowSelectorTemplateContext>) {
UNCOV
668
        this._groupByRowSelectorTemplate = template;
×
669
    }
670

671
    /**
672
     * @hidden @internal
673
     */
674
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
UNCOV
675
        return {
×
676
            $implicit: rowData,
677
            index
678
        };
679
    }
680

681
    /**
682
     * @hidden @internal
683
     */
684
    public detailsViewFocused(container, rowIndex) {
UNCOV
685
        this.navigation.setActiveNode({ row: rowIndex });
×
686
    }
687

688
    /**
689
     * @hidden @internal
690
     */
691
    public override get hasDetails() {
692
        return !!this.detailTemplate;
79✔
693
    }
694

695
    /**
696
     * @hidden @internal
697
     */
698
    public getRowTemplate(rowData) {
699
        if (this.isGroupByRecord(rowData)) {
33!
UNCOV
700
            return this.defaultGroupTemplate;
×
701
        } else if (this.isSummaryRow(rowData)) {
33!
UNCOV
702
            return this.summaryTemplate;
×
703
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
33!
UNCOV
704
            return this.detailTemplateContainer;
×
705
        } else {
706
            return this.recordTemplate;
33✔
707
        }
708
    }
709

710
    /**
711
     * @hidden @internal
712
     */
713
    public override isDetailRecord(record) {
714
        return record && record.detailsData !== undefined;
33✔
715
    }
716

717
    /**
718
     * @hidden @internal
719
     */
720
    public isDetailActive(rowIndex) {
UNCOV
721
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
×
722
    }
723

724
    /**
725
     * Gets/Sets the template reference for the group row.
726
     *
727
     * @example
728
     * ```
729
     * const groupRowTemplate = this.grid.groupRowTemplate;
730
     * this.grid.groupRowTemplate = myRowTemplate;
731
     * ```
732
     */
733
    @Input()
734
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
UNCOV
735
        return this._groupRowTemplate;
×
736
    }
737

738
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
UNCOV
739
        this._groupRowTemplate = template;
×
UNCOV
740
        this.notifyChanges();
×
741
    }
742

743
    /** @hidden @internal */
744
    public trackChanges: (index, rec) => any;
745

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

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

796
    /**
797
     * Returns if a group is expanded or not.
798
     *
799
     * @param group The group record.
800
     * @example
801
     * ```typescript
802
     * public groupRow: IGroupByRecord;
803
     * const expandedGroup = this.grid.isExpandedGroup(this.groupRow);
804
     * ```
805
     */
806
    public override isExpandedGroup(group: IGroupByRecord): boolean {
UNCOV
807
        const state: IGroupByExpandState = this._getStateForGroupRow(group);
×
UNCOV
808
        return state ? state.expanded : this.groupsExpanded;
×
809
    }
810

811
    /**
812
     * Toggles the expansion state of a group.
813
     *
814
     * @param groupRow The group record to toggle.
815
     * @example
816
     * ```typescript
817
     * public groupRow: IGroupByRecord;
818
     * const toggleExpGroup = this.grid.toggleGroup(this.groupRow);
819
     * ```
820
     */
821
    public toggleGroup(groupRow: IGroupByRecord) {
UNCOV
822
        this._toggleGroup(groupRow);
×
UNCOV
823
        this.notifyChanges();
×
824
    }
825

826
    /**
827
     * Select all rows within a group.
828
     *
829
     * @param groupRow: The group record which rows would be selected.
830
     * @param clearCurrentSelection if true clears the current selection
831
     * @example
832
     * ```typescript
833
     * this.grid.selectRowsInGroup(this.groupRow, true);
834
     * ```
835
     */
836
    public selectRowsInGroup(groupRow: IGroupByRecord, clearPrevSelection?: boolean) {
UNCOV
837
        this._gridAPI.groupBy_select_all_rows_in_group(groupRow, clearPrevSelection);
×
UNCOV
838
        this.notifyChanges();
×
839
    }
840

841
    /**
842
     * Deselect all rows within a group.
843
     *
844
     * @param groupRow The group record which rows would be deselected.
845
     * @example
846
     * ```typescript
847
     * public groupRow: IGroupByRecord;
848
     * this.grid.deselectRowsInGroup(this.groupRow);
849
     * ```
850
     */
851
    public deselectRowsInGroup(groupRow: IGroupByRecord) {
UNCOV
852
        this._gridAPI.groupBy_deselect_all_rows_in_group(groupRow);
×
UNCOV
853
        this.notifyChanges();
×
854
    }
855

856
    /**
857
     * Expands the specified group and all of its parent groups.
858
     *
859
     * @param groupRow The group record to fully expand.
860
     * @example
861
     * ```typescript
862
     * public groupRow: IGroupByRecord;
863
     * this.grid.fullyExpandGroup(this.groupRow);
864
     * ```
865
     */
866
    public fullyExpandGroup(groupRow: IGroupByRecord) {
867
        this._fullyExpandGroup(groupRow);
×
868
        this.notifyChanges();
×
869
    }
870

871
    /**
872
     * @hidden @internal
873
     */
874
    public override isGroupByRecord(record: any): boolean {
875
        // return record.records instance of GroupedRecords fails under Webpack
876
        return record && record?.records && record.records?.length &&
66!
877
            record.expression && record.expression?.fieldName;
878
    }
879

880
    /**
881
     * Toggles the expansion state of all group rows recursively.
882
     *
883
     * @example
884
     * ```typescript
885
     * this.grid.toggleAllGroupRows;
886
     * ```
887
     */
888
    public toggleAllGroupRows() {
UNCOV
889
        this.groupingExpansionState = [];
×
UNCOV
890
        this.groupsExpanded = !this.groupsExpanded;
×
UNCOV
891
        this.notifyChanges();
×
892
    }
893

894
    /** @hidden @internal */
895
    public get hasGroupableColumns(): boolean {
896
        return this._columns.some((col) => col.groupable && !col.columnGroup);
150!
897
    }
898

899
    /**
900
     * Returns whether the `IgxGridComponent` has group area.
901
     *
902
     * @example
903
     * ```typescript
904
     * let isGroupAreaVisible = this.grid.showGroupArea;
905
     * ```
906
     *
907
     * @example
908
     * ```html
909
     * <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
910
     * ```
911
     */
912
    @Input({ transform: booleanAttribute })
913
    public get showGroupArea(): boolean {
914
        return this._showGroupArea;
10✔
915
    }
916
    public set showGroupArea(value: boolean) {
UNCOV
917
        this._showGroupArea = value;
×
UNCOV
918
        this.notifyChanges(true);
×
919
    }
920

921
    /**
922
     * @hidden @internal
923
     */
924
    public override isColumnGrouped(fieldName: string): boolean {
UNCOV
925
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
×
926
    }
927

928
    /**
929
     * @hidden @internal
930
     */
931
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
932
        if (this.isDetailRecord(rowData)) {
33!
UNCOV
933
            const cachedData = this.childDetailTemplates.get(rowData.detailsData);
×
UNCOV
934
            const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : rowData.detailsData;
×
UNCOV
935
            if (cachedData) {
×
UNCOV
936
                const view = cachedData.view;
×
UNCOV
937
                const tmlpOutlet = cachedData.owner;
×
UNCOV
938
                return {
×
939
                    $implicit: rowData.detailsData,
940
                    moveView: view,
941
                    owner: tmlpOutlet,
942
                    index: this.dataView.indexOf(rowData),
943
                    templateID: {
944
                        type: 'detailRow',
945
                        id: rowID
946
                    }
947
                };
948
            } else {
949
                // child rows contain unique grids, hence should have unique templates
UNCOV
950
                return {
×
951
                    $implicit: rowData.detailsData,
952
                    templateID: {
953
                        type: 'detailRow',
954
                        id: rowID
955
                    },
956
                    index: this.dataView.indexOf(rowData)
957
                };
958
            }
959
        }
960
        return {
33✔
961
            $implicit: this.isGhostRecord(rowData) || this.isRecordMerged(rowData) ? rowData.recordRef : rowData,
99!
962
            index: this.getDataViewIndex(rowIndex, pinned),
963
            templateID: {
964
                type: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
66!
965
                id: null
966
            },
967
            disabled: this.isGhostRecord(rowData),
968
            metaData: this.isRecordMerged(rowData) ? rowData : null
33!
969
        };
970
    }
971

972
    /**
973
     * @hidden @internal
974
     */
975
    public viewCreatedHandler(args) {
976
        if (args.context.templateID.type === 'detailRow') {
17!
UNCOV
977
            this.childDetailTemplates.set(args.context.$implicit, args);
×
978
        }
979
    }
980

981
    /**
982
     * @hidden @internal
983
     */
984
    public viewMovedHandler(args) {
UNCOV
985
        if (args.context.templateID.type === 'detailRow') {
×
986
            // view was moved, update owner in cache
UNCOV
987
            const key = args.context.$implicit;
×
UNCOV
988
            const cachedData = this.childDetailTemplates.get(key);
×
UNCOV
989
            cachedData.owner = args.owner;
×
990
        }
991
    }
992

993
    /**
994
     * @hidden @internal
995
     */
996
    public get iconTemplate() {
UNCOV
997
        if (this.groupsExpanded) {
×
UNCOV
998
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
×
999
        } else {
UNCOV
1000
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
×
1001
        }
1002
    }
1003

1004
    /**
1005
     * @hidden @internal
1006
     */
1007
    public override ngAfterContentInit() {
1008
        super.ngAfterContentInit();
2✔
1009
        if (this.allowFiltering && this.hasColumnLayouts) {
2!
UNCOV
1010
            this.filterMode = FilterMode.excelStyleFilter;
×
1011
        }
1012
        if (this.groupTemplate) {
2!
UNCOV
1013
            this._groupRowTemplate = this.groupTemplate.template;
×
1014
        }
1015

1016
        if (this.detailTemplateDirective) {
2!
UNCOV
1017
            this._detailTemplate = this.detailTemplateDirective;
×
1018
        }
1019

1020

1021
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
2!
UNCOV
1022
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1023
        }
1024
        this._setupNavigationService();
2✔
1025
    }
1026

1027
    /**
1028
     * @hidden @internal
1029
     */
1030
    public override ngAfterViewInit() {
1031
        super.ngAfterViewInit();
2✔
1032
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
2✔
UNCOV
1033
            const rowData = view.context.$implicit;
×
UNCOV
1034
            if (this.isDetailRecord(rowData)) {
×
UNCOV
1035
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
×
UNCOV
1036
                if (cachedData) {
×
UNCOV
1037
                    const tmlpOutlet = cachedData.owner;
×
UNCOV
1038
                    tmlpOutlet._viewContainerRef.detach(0);
×
1039
                }
1040
            }
1041
        });
1042

1043
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
2✔
UNCOV
1044
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
×
UNCOV
1045
                return;
×
1046
            }
1047

UNCOV
1048
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
×
UNCOV
1049
                const fieldName = sortExpr.fieldName;
×
UNCOV
1050
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
×
UNCOV
1051
                if (groupingExpr) {
×
UNCOV
1052
                    groupingExpr.dir = sortExpr.dir;
×
1053
                }
1054
            });
1055
        });
1056
    }
1057

1058
    /**
1059
     * @hidden @internal
1060
     */
1061
    public override ngOnInit() {
1062
        super.ngOnInit();
2✔
1063
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
54!
1064
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
2✔
UNCOV
1065
            this.crudService.endEdit(false);
×
UNCOV
1066
            this.summaryService.updateSummaryCache(args);
×
UNCOV
1067
            this._headerFeaturesWidth = NaN;
×
1068
        });
1069
    }
1070

1071
    /**
1072
     * @hidden @internal
1073
     */
1074
    public override ngDoCheck(): void {
1075
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
2!
UNCOV
1076
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
×
UNCOV
1077
            if (changes && this._columns.length > 0) {
×
UNCOV
1078
                changes.forEachAddedItem((rec) => {
×
UNCOV
1079
                    const col = this.getColumnByName(rec.item.fieldName);
×
UNCOV
1080
                    if (col) {
×
UNCOV
1081
                        col.hidden = true;
×
1082
                    }
1083
                });
UNCOV
1084
                changes.forEachRemovedItem((rec) => {
×
1085
                    const col = this.getColumnByName(rec.item.fieldName);
×
1086
                    col.hidden = false;
×
1087
                });
1088
            }
1089
        }
1090
        super.ngDoCheck();
2✔
1091
    }
1092

1093
    /**
1094
     * @hidden @internal
1095
     */
1096
    public dataLoading(event) {
UNCOV
1097
        this.dataPreLoad.emit(event);
×
1098
    }
1099

1100
    /**
1101
     *
1102
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
1103
     *
1104
     * @remarks
1105
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
1106
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
1107
     */
1108
    public override getSelectedData(formatters = false, headers = false): any[] {
×
UNCOV
1109
        if (this.groupingExpressions.length || this.hasDetails) {
×
UNCOV
1110
            const source = [];
×
1111

UNCOV
1112
            const process = (record) => {
×
UNCOV
1113
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
×
UNCOV
1114
                    source.push(null);
×
UNCOV
1115
                    return;
×
1116
                }
UNCOV
1117
                source.push(record);
×
1118

1119
            };
1120

UNCOV
1121
            this.dataView.forEach(process);
×
UNCOV
1122
            return this.extractDataFromSelection(source, formatters, headers);
×
1123
        } else {
UNCOV
1124
            return super.getSelectedData(formatters, headers);
×
1125
        }
1126
    }
1127

1128
    /**
1129
     * Returns the `IgxGridRow` by index.
1130
     *
1131
     * @example
1132
     * ```typescript
1133
     * const myRow = grid.getRowByIndex(1);
1134
     * ```
1135
     * @param index
1136
     */
1137
    public getRowByIndex(index: number): RowType {
1138
        let row: RowType;
UNCOV
1139
        if (index < 0) {
×
1140
            return undefined;
×
1141
        }
UNCOV
1142
        if (this.dataView.length >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) {
×
UNCOV
1143
            row = this.createRow(index);
×
1144
        } else {
1145
            if (!(index < this.virtualizationState.startIndex) && !(index > this.virtualizationState.startIndex + this.virtualizationState.chunkSize)) {
×
1146
                row = this.createRow(index);
×
1147
            }
1148
        }
1149

UNCOV
1150
        if (this.pagingMode === 'remote' && this.page !== 0) {
×
UNCOV
1151
            row.index = index + this.perPage * this.page;
×
1152
        }
UNCOV
1153
        return row;
×
1154
    }
1155

1156
    /**
1157
     * Returns `IgxGridRow` object by the specified primary key.
1158
     *
1159
     * @remarks
1160
     * Requires that the `primaryKey` property is set.
1161
     * @example
1162
     * ```typescript
1163
     * const myRow = this.grid1.getRowByKey("cell5");
1164
     * ```
1165
     * @param keyValue
1166
     */
1167
    public getRowByKey(key: any): RowType {
UNCOV
1168
        const rec = this.filteredSortedData ? this.primaryKey ?
×
UNCOV
1169
            this.filteredSortedData.find(record => record[this.primaryKey] === key) :
×
UNCOV
1170
            this.filteredSortedData.find(record => record === key) : undefined;
×
UNCOV
1171
        const index = this.dataView.indexOf(rec);
×
UNCOV
1172
        if (index < 0 || index > this.dataView.length) {
×
UNCOV
1173
            return undefined;
×
1174
        }
1175

UNCOV
1176
        return new IgxGridRow(this, index, rec);
×
1177
    }
1178

1179
    /**
1180
     * @hidden @internal
1181
     */
1182
    public allRows(): RowType[] {
UNCOV
1183
        return this.dataView.map((rec, index) => {
×
UNCOV
1184
            this.pagingMode === 'remote' && this.page !== 0 ?
×
1185
                index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
UNCOV
1186
            return this.createRow(index);
×
1187
        });
1188
    }
1189

1190
    /**
1191
     * Returns the collection of `IgxGridRow`s for current page.
1192
     *
1193
     * @hidden @internal
1194
     */
1195
    public dataRows(): RowType[] {
UNCOV
1196
        return this.allRows().filter(row => row instanceof IgxGridRow);
×
1197
    }
1198

1199
    /**
1200
     * Returns an array of the selected `IgxGridCell`s.
1201
     *
1202
     * @example
1203
     * ```typescript
1204
     * const selectedCells = this.grid.selectedCells;
1205
     * ```
1206
     */
1207
    public get selectedCells(): CellType[] {
UNCOV
1208
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
×
UNCOV
1209
            .reduce((a, b) => a.concat(b), []);
×
1210
    }
1211

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

1233
    /**
1234
     * Returns a `CellType` object that matches the conditions.
1235
     *
1236
     * @remarks
1237
     * Requires that the primaryKey property is set.
1238
     * @example
1239
     * ```typescript
1240
     * grid.getCellByKey(1, 'index');
1241
     * ```
1242
     * @param rowSelector match any rowID
1243
     * @param columnField
1244
     */
1245
    public getCellByKey(rowSelector: any, columnField: string): CellType {
UNCOV
1246
        const row = this.getRowByKey(rowSelector);
×
UNCOV
1247
        const column = this._columns.find((col) => col.field === columnField);
×
UNCOV
1248
        if (row && column) {
×
UNCOV
1249
            return new IgxGridCell(this, row.index, column);
×
1250
        }
1251
    }
1252

1253
    public override pinRow(rowID: any, index?: number): boolean {
UNCOV
1254
        const row = this.getRowByKey(rowID);
×
UNCOV
1255
        return super.pinRow(rowID, index, row);
×
1256
    }
1257

1258
    public override unpinRow(rowID: any): boolean {
UNCOV
1259
        const row = this.getRowByKey(rowID);
×
UNCOV
1260
        return super.unpinRow(rowID, row);
×
1261
    }
1262

1263
    /**
1264
     * @hidden @internal
1265
     */
1266
    public createRow(index: number, data?: any): RowType {
1267
        let row: RowType;
1268

UNCOV
1269
        const dataIndex = this._getDataViewIndex(index);
×
UNCOV
1270
        const rec = data ?? this.dataView[dataIndex];
×
1271

UNCOV
1272
        if (rec && this.isGroupByRecord(rec)) {
×
UNCOV
1273
            row = new IgxGroupByRow(this, index, rec);
×
1274
        }
UNCOV
1275
        if (rec && this.isSummaryRow(rec)) {
×
UNCOV
1276
            row = new IgxSummaryRow(this, index, rec.summaries);
×
1277
        }
1278
        // if found record is a no a groupby or summary row, return IgxGridRow instance
UNCOV
1279
        if (!row && rec) {
×
UNCOV
1280
            row = new IgxGridRow(this, index, rec);
×
1281
        }
1282

UNCOV
1283
        return row;
×
1284
    }
1285

1286
    /**
1287
     * @hidden @internal
1288
     */
1289
    protected override get defaultTargetBodyHeight(): number {
UNCOV
1290
        const allItems = this.totalItemCount || this.dataLength;
×
UNCOV
1291
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
×
1292
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
×
1293
    }
1294

1295
    /**
1296
     * @hidden @internal
1297
     */
1298
    protected override getGroupAreaHeight(): number {
1299
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
3!
1300
    }
1301

1302
    /**
1303
     * @hidden @internal
1304
     */
1305
    protected override onColumnsAddedOrRemoved() {
1306
        // update grouping states
UNCOV
1307
        this.groupablePipeTrigger++;
×
UNCOV
1308
        if (this.groupingExpressions && this.hideGroupedColumns) {
×
UNCOV
1309
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1310
        }
UNCOV
1311
        super.onColumnsAddedOrRemoved();
×
1312
    }
1313

1314
    /**
1315
     * @hidden
1316
     */
1317
    protected override onColumnsChanged(change: QueryList<IgxColumnComponent>) {
UNCOV
1318
        super.onColumnsChanged(change);
×
1319

UNCOV
1320
        if (this.hasColumnLayouts && !(this.navigation instanceof IgxGridMRLNavigationService)) {
×
1321
            this._setupNavigationService();
×
1322
        }
1323
    }
1324

1325
    /**
1326
     * @hidden @internal
1327
     */
1328
    protected override scrollTo(row: any | number, column: any | number): void {
UNCOV
1329
        if (this.groupingExpressions && this.groupingExpressions.length
×
1330
            && typeof (row) !== 'number') {
UNCOV
1331
            const rowIndex = this.groupingResult.indexOf(row);
×
UNCOV
1332
            const groupByRecord = this.groupingMetadata[rowIndex];
×
UNCOV
1333
            if (groupByRecord) {
×
UNCOV
1334
                this._fullyExpandGroup(groupByRecord);
×
1335
            }
1336
        }
1337

UNCOV
1338
        super.scrollTo(row, column, this.groupingFlatResult);
×
1339
    }
1340

1341
    /**
1342
     * @hidden @internal
1343
     */
1344
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
UNCOV
1345
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
×
1346
    }
1347

1348
    /**
1349
     * @hidden
1350
     */
1351
    protected _toggleGroup(groupRow: IGroupByRecord) {
UNCOV
1352
        this._gridAPI.groupBy_toggle_group(groupRow);
×
1353
    }
1354

1355
    /**
1356
     * @hidden @internal
1357
     */
1358
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
UNCOV
1359
        this._gridAPI.groupBy_fully_expand_group(groupRow);
×
1360
    }
1361

1362
    /**
1363
     * @hidden @internal
1364
     */
1365
    protected _applyGrouping() {
UNCOV
1366
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
×
1367
    }
1368

1369
    protected _setupNavigationService() {
1370
        if (this.hasColumnLayouts) {
2!
UNCOV
1371
            this.navigation = this.injector.get(IgxGridMRLNavigationService);
×
UNCOV
1372
            this.navigation.grid = this;
×
1373
        }
1374
    }
1375

1376
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
UNCOV
1377
        if (expression instanceof Array) {
×
UNCOV
1378
            for (const singleExpression of expression) {
×
UNCOV
1379
                if (!singleExpression.fieldName) {
×
UNCOV
1380
                    return true;
×
1381
                }
1382
            }
UNCOV
1383
            return false;
×
1384
        }
UNCOV
1385
        return !expression.fieldName;
×
1386
    }
1387

1388
    private _setGroupColsVisibility(value) {
UNCOV
1389
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
×
UNCOV
1390
            this.groupingExpressions.forEach((expr) => {
×
UNCOV
1391
                const col = this.getColumnByName(expr.fieldName);
×
UNCOV
1392
                col.hidden = value;
×
1393
            });
1394
        }
1395
    }
1396

1397
    private stringifyCallback(key: string, val: any) {
1398
        // Workaround for Blazor, since its wrappers inject this externalObject that cannot serialize.
UNCOV
1399
        if (key === 'externalObject') {
×
1400
            return undefined;
×
1401
        }
UNCOV
1402
        return val;
×
1403
    }
1404
}
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