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

IgniteUI / igniteui-angular / 20960087204

13 Jan 2026 02:19PM UTC coverage: 12.713% (-78.8%) from 91.5%
20960087204

Pull #16746

github

web-flow
Merge 9afce6e5d into a967f087e
Pull Request #16746: fix(csv): export summaries - master

1008 of 16803 branches covered (6.0%)

19 of 23 new or added lines in 2 files covered. (82.61%)

24693 existing lines in 336 files now uncovered.

3985 of 31345 relevant lines covered (12.71%)

2.49 hits per line

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

22.15
/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
    IgxGridGroupByAreaComponent,
25
    IgxGridHeaderRowComponent,
26
    IgxGridMasterDetailContext,
27
    IgxGridMRLNavigationService,
28
    IgxGridNavigationService,
29
    IgxGridRow,
30
    IgxGridRowClassesPipe,
31
    IgxGridRowPinningPipe,
32
    IgxGridRowStylesPipe,
33
    IgxGridSelectionService,
34
    IgxGridSummaryService,
35
    IgxGridTransactionPipe,
36
    IgxGridValidationService,
37
    IgxGroupByRow,
38
    IgxGroupByRowSelectorDirective,
39
    IgxGroupByRowSelectorTemplateContext,
40
    IgxGroupByRowTemplateContext,
41
    IgxGroupByRowTemplateDirective,
42
    IgxHasVisibleColumnsPipe,
43
    IgxRowEditTabStopDirective,
44
    IgxStringReplacePipe,
45
    IgxSummaryDataPipe,
46
    IgxSummaryRow,
47
    IgxSummaryRowComponent,
48
    RowType
49
} from 'igniteui-angular/grids/core';
50
import { IgxGridAPIService } from './grid-api.service';
51
import { IgxGridGroupByRowComponent } from './groupby-row.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
        IgxGridNavigationService,
113
        IgxGridSummaryService,
114
        IgxGridSelectionService,
115
        IgxGridValidationService,
116
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxGridAPIService },
117
        { provide: IGX_GRID_BASE, useExisting: IgxGridComponent },
118
        IgxFilteringService,
119
        IgxColumnResizingService,
120
        IgxForOfSyncService,
121
        IgxForOfScrollSyncService,
122
    ],
123
    selector: 'igx-grid',
124
    templateUrl: './grid.component.html',
125
    imports: [
126
        NgClass,
127
        NgStyle,
128
        NgTemplateOutlet,
129
        IgxGridGroupByAreaComponent,
130
        IgxGridHeaderRowComponent,
131
        IgxGridBodyDirective,
132
        IgxGridDragSelectDirective,
133
        IgxColumnMovingDropDirective,
134
        IgxGridForOfDirective,
135
        IgxTemplateOutletDirective,
136
        IgxGridRowComponent,
137
        IgxGridGroupByRowComponent,
138
        IgxSummaryRowComponent,
139
        IgxOverlayOutletDirective,
140
        IgxToggleDirective,
141
        IgxCircularProgressBarComponent,
142
        IgxSnackbarComponent,
143
        IgxButtonDirective,
144
        IgxRippleDirective,
145
        IgxIconComponent,
146
        IgxRowEditTabStopDirective,
147
        IgxGridColumnResizerComponent,
148
        IgxGridTransactionPipe,
149
        IgxHasVisibleColumnsPipe,
150
        IgxGridRowPinningPipe,
151
        IgxGridAddRowPipe,
152
        IgxGridRowClassesPipe,
153
        IgxGridRowStylesPipe,
154
        IgxSummaryDataPipe,
155
        IgxGridGroupingPipe,
156
        IgxGridPagingPipe,
157
        IgxGridSortingPipe,
158
        IgxGridFilteringPipe,
159
        IgxGridSummaryPipe,
160
        IgxGridDetailsPipe,
161
        IgxStringReplacePipe,
162
        IgxGridCellMergePipe,
163
        IgxGridUnmergeActivePipe,
164
        IgxScrollInertiaDirective
165
    ],
166
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
167
})
168
export class IgxGridComponent extends IgxGridBaseDirective implements GridType, OnInit, DoCheck, AfterContentInit, AfterViewInit {
3✔
169
    /**
170
     * Emitted when a new chunk of data is loaded from virtualization.
171
     *
172
     * @example
173
     * ```typescript
174
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataPreLoad)='handleDataPreloadEvent()'></igx-grid>
175
     * ```
176
     */
177
    @Output()
178
    public dataPreLoad = new EventEmitter<IForOfState>();
1✔
179

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

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

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

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

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

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

257

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

463
    private childDetailTemplates: Map<any, any> = new Map();
1✔
464

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1019

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

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

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

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

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

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

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

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

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

1118
            };
1119

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
1282
        return row;
×
1283
    }
1284

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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