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

IgniteUI / igniteui-angular / 13545721611

26 Feb 2025 02:05PM UTC coverage: 91.591% (-0.03%) from 91.622%
13545721611

Pull #15209

github

web-flow
Merge 25ba2c0ab into a038a03b1
Pull Request #15209: fix(pivot-grid): added createRow method for grid based events

13329 of 15614 branches covered (85.37%)

0 of 18 new or added lines in 2 files covered. (0.0%)

611 existing lines in 53 files now uncovered.

26883 of 29351 relevant lines covered (91.59%)

33690.24 hits per line

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

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

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

58
let NEXT_ID = 0;
2✔
59

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

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

170
    /**
171
     * @hidden
172
     */
173
    @Output()
174
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
1,994✔
175

176
    /**
177
     * @hidden @internal
178
     */
179
    @Output()
180
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
1,994✔
181

182
    /**
183
     * Emitted when columns are grouped/ungrouped.
184
     *
185
     * @remarks
186
     * The `groupingDone` event would be raised only once if several columns get grouped at once by calling
187
     * the `groupBy()` or `clearGrouping()` API methods and passing an array as an argument.
188
     * The event arguments provide the `expressions`, `groupedColumns` and `ungroupedColumns` properties, which contain
189
     * the `ISortingExpression` and the `IgxColumnComponent` related to the grouping/ungrouping operation.
190
     * Please note that `groupedColumns` and `ungroupedColumns` show only the **newly** changed columns (affected by the **last**
191
     * grouping/ungrouping operation), not all columns which are currently grouped/ungrouped.
192
     * columns.
193
     * @example
194
     * ```html
195
     * <igx-grid #grid [data]="localData" (groupingDone)="groupingDone($event)" [autoGenerate]="true"></igx-grid>
196
     * ```
197
     */
198
    @Output()
199
    public groupingDone = new EventEmitter<IGroupingDoneEventArgs>();
1,994✔
200

201
    /**
202
     * Gets/Sets whether created groups are rendered expanded or collapsed.
203
     *
204
     * @remarks
205
     * The default rendered state is expanded.
206
     * @example
207
     * ```html
208
     * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid>
209
     * ```
210
     */
211
    @Input({ transform: booleanAttribute })
212
    public groupsExpanded = true;
1,994✔
213

214
    /**
215
     * Gets/Sets the template that will be rendered as a GroupBy drop area.
216
     *
217
     * @remarks
218
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
219
     * @example
220
     * ```html
221
     * <igx-grid [dropAreaTemplate]="dropAreaRef">
222
     * </igx-grid>
223
     * <ng-template #myDropArea>
224
     *      <span> Custom drop area! </span>
225
     * </ng-template>
226
     * ```
227
     */
228
    @Input()
229
    public dropAreaTemplate: TemplateRef<void>;
230

231
    /**
232
     * @hidden @internal
233
     */
234
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
235
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
236

237

238
    /**
239
     * Returns a reference to the master-detail template.
240
     * ```typescript
241
     * let detailTemplate = this.grid.detailTemplate;
242
     * ```
243
     *
244
     * @memberof IgxColumnComponent
245
     */
246
    @Input('detailTemplate')
247
    public get detailTemplate(): TemplateRef<IgxGridMasterDetailContext> {
248
        return this._detailTemplate;
305,601✔
249
    }
250
    /**
251
     * Sets the master-detail template.
252
     * ```html
253
     * <ng-template #detailTemplate igxGridDetail let-dataItem>
254
     *    <div>
255
     *       <div><span class='categoryStyle'>City:</span> {{dataItem.City}}</div>
256
     *       <div><span class='categoryStyle'>Address:</span> {{dataItem.Address}}</div>
257
     *    </div>
258
     * </ng-template>
259
     * ```
260
     * ```typescript
261
     * @ViewChild("'detailTemplate'", {read: TemplateRef })
262
     * public detailTemplate: TemplateRef<any>;
263
     * this.grid.detailTemplate = this.detailTemplate;
264
     * ```
265
     *
266
     * @memberof IgxColumnComponent
267
     */
268
    public set detailTemplate(template: TemplateRef<IgxGridMasterDetailContext>) {
269
        this._detailTemplate = template;
1✔
270
    }
271

272
    /**
273
     * @hidden @internal
274
     */
275
    @HostBinding('attr.role')
276
    public role = 'grid';
1,994✔
277

278
    /**
279
     * Gets/Sets the value of the `id` attribute.
280
     *
281
     * @remarks
282
     * If not provided it will be automatically generated.
283
     * @example
284
     * ```html
285
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid>
286
     * ```
287
     */
288
    @HostBinding('attr.id')
289
    @Input()
290
    public id = `igx-grid-${NEXT_ID++}`;
1,994✔
291

292
    /**
293
     * @hidden @internal
294
     */
295
    @ViewChild('record_template', { read: TemplateRef, static: true })
296
    protected recordTemplate: TemplateRef<any>;
297

298
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
299
    protected detailTemplateContainer: TemplateRef<any>;
300

301
    @ViewChild('group_template', { read: TemplateRef, static: true })
302
    protected defaultGroupTemplate: TemplateRef<any>;
303

304
    @ViewChild('summary_template', { read: TemplateRef, static: true })
305
    protected summaryTemplate: TemplateRef<any>;
306

307
    /**
308
     * @hidden @internal
309
     */
310
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
311
    protected groupTemplate: IgxGroupByRowTemplateDirective;
312

313
    /**
314
     * @hidden
315
     * @internal
316
     */
317
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
318
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
319

320
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
321
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
322

323
    private _groupsRecords: IGroupByRecord[] = [];
1,994✔
324
    /**
325
     * Gets the hierarchical representation of the group by records.
326
     *
327
     * @example
328
     * ```typescript
329
     * let groupRecords = this.grid.groupsRecords;
330
     * ```
331
     */
332
    public get groupsRecords(): IGroupByRecord[] {
333
        return this._groupsRecords;
25,385✔
334
    }
335

336
    /**
337
     * @hidden @internal
338
     * Includes children of collapsed group rows.
339
     */
340
    public groupingResult: any[];
341

342
    /**
343
     * @hidden @internal
344
     */
345
    public groupingMetadata: any[];
346

347
    /**
348
     * @hidden @internal
349
     * Does not include children of collapsed group rows.
350
     */
351
    public groupingFlatResult: any[];
352
    /**
353
     * @hidden
354
     */
355
    protected _groupingExpressions: IGroupingExpression[] = [];
1,994✔
356
    /**
357
     * @hidden
358
     */
359
    protected _groupingExpandState: IGroupByExpandState[] = [];
1,994✔
360
    /**
361
     * @hidden
362
     */
363
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
364

365
    /**
366
     * @hidden
367
     */
368
    protected _groupStrategy: IGridGroupingStrategy;
369
    /**
370
     * @hidden
371
     */
372
    protected groupingDiffer;
373
    private _data?: any[] | null;
374
    private _hideGroupedColumns = false;
1,994✔
375
    private _dropAreaMessage = null;
1,994✔
376
    private _showGroupArea = true;
1,994✔
377

378
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
379
    private _detailTemplate;
380

381

382
    /**
383
     * Gets/Sets the array of data that populates the component.
384
     *
385
     * @example
386
     * ```html
387
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
388
     * ```
389
     */
390
    /* treatAsRef */
391
    @Input()
392
    public get data(): any[] | null {
393
        return this._data;
125,554✔
394
    }
395

396
    public set data(value: any[] | null) {
397
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
2,082✔
398
        const oldData = this._data;
2,082✔
399
        this._data = value || [];
2,082✔
400
        this.summaryService.clearSummaryCache();
2,082✔
401
        if (!this._init) {
2,082✔
402
            this.validation.updateAll(this._data);
103✔
403
        }
404

405
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
2,082✔
406
            this.setupColumns();
20✔
407
        }
408

409
        this.cdr.markForCheck();
2,082✔
410
        if (this.isPercentHeight) {
2,082✔
411
            this.notifyChanges(true);
922✔
412
        }
413
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
414
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
2,082✔
415
            this.recalculateAutoSizes();
1✔
416
        }
417
        this.checkPrimaryKeyField();
2,082✔
418
    }
419

420
    /**
421
     * Gets/Sets the total number of records in the data source.
422
     *
423
     * @remarks
424
     * This property is required for remote grid virtualization to function when it is bound to remote data.
425
     * @example
426
     * ```typescript
427
     * const itemCount = this.grid1.totalItemCount;
428
     * this.grid1.totalItemCount = 55;
429
     * ```
430
     */
431
    @Input()
432
    public set totalItemCount(count) {
433
        this.verticalScrollContainer.totalItemCount = count;
4✔
434
    }
435

436
    public get totalItemCount() {
437
        return this.verticalScrollContainer.totalItemCount;
1,232✔
438
    }
439

440
    private get _gridAPI(): IgxGridAPIService {
441
        return this.gridAPI as IgxGridAPIService;
14,532✔
442
    }
443

444
    private childDetailTemplates: Map<any, any> = new Map();
1,994✔
445

446
    /**
447
     * @hidden @internal
448
     */
449
    public groupingPerformedSubject = new Subject<void>();
1,994✔
450

451
    /**
452
     * @hidden @internal
453
     */
454
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
1,994✔
455

456
    /* mustSetInCodePlatforms: WebComponents;Blazor;React */
457
    /**
458
     * Gets/Sets the group by state.
459
     *
460
     * @example
461
     * ```typescript
462
     * let groupByState = this.grid.groupingExpressions;
463
     * this.grid.groupingExpressions = [...];
464
     * ```
465
     * @remarks
466
     * Supports two-way data binding.
467
     * @example
468
     * ```html
469
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid>
470
     * ```
471
     */
472
    @Input()
473
    public get groupingExpressions(): IGroupingExpression[] {
474
        return this._groupingExpressions;
1,830,189✔
475
    }
476

477
    public set groupingExpressions(value: IGroupingExpression[]) {
478
        if (this.groupingExpressions === value) {
612✔
479
            return;
114✔
480
        }
481
        if (value && value.length > 10) {
498✔
482
            throw Error('Maximum amount of grouped columns is 10.');
1✔
483
        }
484
        const oldExpressions: IGroupingExpression[] = this.groupingExpressions;
497✔
485
        const newExpressions: IGroupingExpression[] = value;
497✔
486
        this._groupingExpressions = cloneArray(value);
497✔
487
        this.groupingExpressionsChange.emit(this._groupingExpressions);
497✔
488
        if (this._gridAPI.grid) {
497✔
489
            /* grouping and sorting are working separate from each other */
490
            this._applyGrouping();
399✔
491
            this.notifyChanges();
399✔
492
        }
493
        if (!this._init && JSON.stringify(oldExpressions, this.stringifyCallback) !== JSON.stringify(newExpressions, this.stringifyCallback) && this._columns) {
497✔
494
            const groupedCols: IgxColumnComponent[] = [];
268✔
495
            const ungroupedCols: IgxColumnComponent[] = [];
268✔
496
            const groupedColsArr = newExpressions.filter((obj) => !oldExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
321✔
497
            groupedColsArr.forEach((elem) => {
268✔
498
                groupedCols.push(this.getColumnByName(elem.fieldName));
270✔
499
            }, this);
500
            const ungroupedColsArr = oldExpressions.filter((obj) => !newExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
268✔
501
            ungroupedColsArr.forEach((elem) => {
268✔
502
                ungroupedCols.push(this.getColumnByName(elem.fieldName));
26✔
503
            }, this);
504
            this.notifyChanges();
268✔
505
            const groupingDoneArgs: IGroupingDoneEventArgs = {
268✔
506
                expressions: newExpressions,
507
                groupedColumns: groupedCols,
508
                ungroupedColumns: ungroupedCols
509
            };
510
            this.groupingPerformed$.pipe(take(1)).subscribe(() => {
268✔
511
                this.groupingDone.emit(groupingDoneArgs);
263✔
512
            });
513
        }
514
    }
515

516
    /**
517
     * Gets/Sets a list of expansion states for group rows.
518
     *
519
     * @remarks
520
     * Includes only states that differ from the default one (controlled through groupsExpanded and states that the user has changed.
521
     * Contains the expansion state (expanded: boolean) and the unique identifier for the group row (Array).
522
     * Supports two-way data binding.
523
     * @example
524
     * ```html
525
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpansionState)]="model.groupingExpansionState"></igx-grid>
526
     * ```
527
     */
528
    @Input()
529
    public get groupingExpansionState() {
530
        return this._groupingExpandState;
39,511✔
531
    }
532

533
    public set groupingExpansionState(value) {
534
        if (value !== this._groupingExpandState) {
95✔
535
            this.groupingExpansionStateChange.emit(value);
95✔
536
        }
537
        this._groupingExpandState = value;
95✔
538
        if (this.gridAPI.grid) {
95✔
539
            this.cdr.detectChanges();
95✔
540
        }
541
    }
542

543
    /**
544
     * Gets/Sets whether the grouped columns should be hidden.
545
     *
546
     * @remarks
547
     * The default value is "false"
548
     * @example
549
     * ```html
550
     * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid>
551
     * ```
552
     */
553
    @Input({ transform: booleanAttribute })
554
    public get hideGroupedColumns() {
555
        return this._hideGroupedColumns;
2,075✔
556
    }
557

558
    public set hideGroupedColumns(value: boolean) {
559
        if (value) {
36✔
560
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
16✔
561
        } else {
562
            this.groupingDiffer = null;
20✔
563
        }
564
        if (this._columns && this.groupingExpressions) {
36✔
565
            this._setGroupColsVisibility(value);
36✔
566
        }
567

568
        this._hideGroupedColumns = value;
36✔
569
    }
570

571
    /**
572
     * Gets/Sets the grouping strategy of the grid.
573
     *
574
     * @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
575
     *
576
     * @example
577
     * ```html
578
     *  <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
579
     * ```
580
     */
581
    @Input()
582
    public get groupStrategy(): IGridGroupingStrategy {
583
        return this._groupStrategy;
25,362✔
584
    }
585

586
    public set groupStrategy(value: IGridGroupingStrategy) {
587
        this._groupStrategy = value;
13✔
588
    }
589

590
    /**
591
     * Gets/Sets the message displayed inside the GroupBy drop area where columns can be dragged on.
592
     *
593
     * @remarks
594
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
595
     * @example
596
     * ```html
597
     * <igx-grid dropAreaMessage="Drop here to group!">
598
     *      <igx-column [groupable]="true" field="ID"></igx-column>
599
     * </igx-grid>
600
     * ```
601
     */
602
    @Input()
603
    public set dropAreaMessage(value: string) {
604
        this._dropAreaMessage = value;
1✔
605
        this.notifyChanges();
1✔
606
    }
607

608
    public get dropAreaMessage(): string {
609
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
3,861✔
610
    }
611

612
    /**
613
     * @hidden @internal
614
     */
615
    public get groupsRowList() {
616
        const res = new QueryList<any>();
147✔
617
        if (!this._groupsRowList) {
147!
UNCOV
618
            return res;
×
619
        }
620
        const rList = this._groupsRowList.filter(item => item.element.nativeElement.parentElement !== null)
629✔
621
            .sort((item1, item2) => item1.index - item2.index);
607✔
622
        res.reset(rList);
147✔
623
        return res;
147✔
624
    }
625

626
    /**
627
     * Gets the group by row selector template.
628
     */
629
    @Input()
630
    public get groupByRowSelectorTemplate(): TemplateRef<IgxGroupByRowSelectorTemplateContext> {
631
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
389✔
632
    }
633

634
    /**
635
     * Sets the group by row selector template.
636
     * ```html
637
     * <ng-template #template igxGroupByRowSelector let-groupByRowContext>
638
     * {{ groupByRowContext.selectedCount }} / {{ groupByRowContext.totalCount  }}
639
     * </ng-template>
640
     * ```
641
     * ```typescript
642
     * @ViewChild("'template'", {read: TemplateRef })
643
     * public template: TemplateRef<any>;
644
     * this.grid.groupByRowSelectorTemplate = this.template;
645
     * ```
646
     */
647
    public set groupByRowSelectorTemplate(template: TemplateRef<IgxGroupByRowSelectorTemplateContext>) {
648
        this._groupByRowSelectorTemplate = template;
1✔
649
    }
650

651
    /**
652
     * @hidden @internal
653
     */
654
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
655
        return {
2,383✔
656
            $implicit: rowData,
657
            index
658
        };
659
    }
660

661
    /**
662
     * @hidden @internal
663
     */
664
    public detailsViewFocused(container, rowIndex) {
665
        this.navigation.setActiveNode({ row: rowIndex });
2✔
666
    }
667

668
    /**
669
     * @hidden @internal
670
     */
671
    public override get hasDetails() {
672
        return !!this.detailTemplate;
303,218✔
673
    }
674

675
    /**
676
     * @hidden @internal
677
     */
678
    public getRowTemplate(rowData) {
679
        if (this.isGroupByRecord(rowData)) {
149,072✔
680
            return this.defaultGroupTemplate;
9,189✔
681
        } else if (this.isSummaryRow(rowData)) {
139,883✔
682
            return this.summaryTemplate;
1,510✔
683
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
138,373✔
684
            return this.detailTemplateContainer;
2,383✔
685
        } else {
686
            return this.recordTemplate;
135,990✔
687
        }
688
    }
689

690
    /**
691
     * @hidden @internal
692
     */
693
    public override isDetailRecord(record) {
694
        return record && record.detailsData !== undefined;
163,228✔
695
    }
696

697
    /**
698
     * @hidden @internal
699
     */
700
    public isDetailActive(rowIndex) {
701
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
2,383!
702
    }
703

704
    /**
705
     * Gets/Sets the template reference for the group row.
706
     *
707
     * @example
708
     * ```
709
     * const groupRowTemplate = this.grid.groupRowTemplate;
710
     * this.grid.groupRowTemplate = myRowTemplate;
711
     * ```
712
     */
713
    @Input()
714
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
715
        return this._groupRowTemplate;
2,224✔
716
    }
717

718
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
719
        this._groupRowTemplate = template;
1✔
720
        this.notifyChanges();
1✔
721
    }
722

723
    /** @hidden @internal */
724
    public trackChanges: (index, rec) => any;
725

726
    /**
727
     * Groups by a new `IgxColumnComponent` based on the provided expression, or modifies an existing one.
728
     *
729
     * @remarks
730
     * Also allows for multiple columns to be grouped at once if an array of `ISortingExpression` is passed.
731
     * The `groupingDone` event would get raised only **once** if this method gets called multiple times with the same arguments.
732
     * @example
733
     * ```typescript
734
     * this.grid.groupBy({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
735
     * this.grid.groupBy([
736
     *     { fieldName: name1, dir: SortingDirection.Asc, ignoreCase: false },
737
     *     { fieldName: name2, dir: SortingDirection.Desc, ignoreCase: true },
738
     *     { fieldName: name3, dir: SortingDirection.Desc, ignoreCase: false }
739
     * ]);
740
     * ```
741
     */
742
    public groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void {
743
        if (this.checkIfNoColumnField(expression)) {
231✔
744
            return;
1✔
745
        }
746
        this.crudService.endEdit(false);
230✔
747
        if (expression instanceof Array) {
230✔
748
            this._gridAPI.groupBy_multiple(expression);
20✔
749
        } else {
750
            this._gridAPI.groupBy(expression);
210✔
751
        }
752
        this.notifyChanges(true);
229✔
753
    }
754

755
    /**
756
     * Clears grouping for particular column, array of columns or all columns.
757
     *
758
     * @remarks
759
     * Clears all grouping in the grid, if no parameter is passed.
760
     * If a parameter is provided, clears grouping for a particular column or an array of columns.
761
     * @example
762
     * ```typescript
763
     * this.grid.clearGrouping(); //clears all grouping
764
     * this.grid.clearGrouping("ID"); //ungroups a single column
765
     * this.grid.clearGrouping(["ID", "Column1", "Column2"]); //ungroups multiple columns
766
     * ```
767
     * @param name Name of column or array of column names to be ungrouped.
768
     */
769
    public clearGrouping(name?: string | Array<string>): void {
770
        this._gridAPI.clear_groupby(name);
18✔
771
        this.calculateGridSizes();
18✔
772
        this.notifyChanges(true);
18✔
773
        this.groupingPerformedSubject.next();
18✔
774
    }
775

776
    /**
777
     * Returns if a group is expanded or not.
778
     *
779
     * @param group The group record.
780
     * @example
781
     * ```typescript
782
     * public groupRow: IGroupByRecord;
783
     * const expandedGroup = this.grid.isExpandedGroup(this.groupRow);
784
     * ```
785
     */
786
    public override isExpandedGroup(group: IGroupByRecord): boolean {
787
        const state: IGroupByExpandState = this._getStateForGroupRow(group);
13,293✔
788
        return state ? state.expanded : this.groupsExpanded;
13,293✔
789
    }
790

791
    /**
792
     * Toggles the expansion state of a group.
793
     *
794
     * @param groupRow The group record to toggle.
795
     * @example
796
     * ```typescript
797
     * public groupRow: IGroupByRecord;
798
     * const toggleExpGroup = this.grid.toggleGroup(this.groupRow);
799
     * ```
800
     */
801
    public toggleGroup(groupRow: IGroupByRecord) {
802
        this._toggleGroup(groupRow);
58✔
803
        this.notifyChanges();
58✔
804
    }
805

806
    /**
807
     * Select all rows within a group.
808
     *
809
     * @param groupRow: The group record which rows would be selected.
810
     * @param clearCurrentSelection if true clears the current selection
811
     * @example
812
     * ```typescript
813
     * this.grid.selectRowsInGroup(this.groupRow, true);
814
     * ```
815
     */
816
    public selectRowsInGroup(groupRow: IGroupByRecord, clearPrevSelection?: boolean) {
817
        this._gridAPI.groupBy_select_all_rows_in_group(groupRow, clearPrevSelection);
4✔
818
        this.notifyChanges();
4✔
819
    }
820

821
    /**
822
     * Deselect all rows within a group.
823
     *
824
     * @param groupRow The group record which rows would be deselected.
825
     * @example
826
     * ```typescript
827
     * public groupRow: IGroupByRecord;
828
     * this.grid.deselectRowsInGroup(this.groupRow);
829
     * ```
830
     */
831
    public deselectRowsInGroup(groupRow: IGroupByRecord) {
832
        this._gridAPI.groupBy_deselect_all_rows_in_group(groupRow);
2✔
833
        this.notifyChanges();
2✔
834
    }
835

836
    /**
837
     * Expands the specified group and all of its parent groups.
838
     *
839
     * @param groupRow The group record to fully expand.
840
     * @example
841
     * ```typescript
842
     * public groupRow: IGroupByRecord;
843
     * this.grid.fullyExpandGroup(this.groupRow);
844
     * ```
845
     */
846
    public fullyExpandGroup(groupRow: IGroupByRecord) {
UNCOV
847
        this._fullyExpandGroup(groupRow);
×
UNCOV
848
        this.notifyChanges();
×
849
    }
850

851
    /**
852
     * @hidden @internal
853
     */
854
    public override isGroupByRecord(record: any): boolean {
855
        // return record.records instance of GroupedRecords fails under Webpack
856
        return record && record?.records && record.records?.length &&
357,876✔
857
            record.expression && record.expression?.fieldName;
858
    }
859

860
    /**
861
     * Toggles the expansion state of all group rows recursively.
862
     *
863
     * @example
864
     * ```typescript
865
     * this.grid.toggleAllGroupRows;
866
     * ```
867
     */
868
    public toggleAllGroupRows() {
869
        this.groupingExpansionState = [];
16✔
870
        this.groupsExpanded = !this.groupsExpanded;
16✔
871
        this.notifyChanges();
16✔
872
    }
873

874
    /** @hidden @internal */
875
    public get hasGroupableColumns(): boolean {
876
        return this._columns.some((col) => col.groupable && !col.columnGroup);
154,458✔
877
    }
878

879
    /**
880
     * Returns whether the `IgxGridComponent` has group area.
881
     *
882
     * @example
883
     * ```typescript
884
     * let isGroupAreaVisible = this.grid.showGroupArea;
885
     * ```
886
     *
887
     * @example
888
     * ```html
889
     * <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
890
     * ```
891
     */
892
    @Input({ transform: booleanAttribute })
893
    public get showGroupArea(): boolean {
894
        return this._showGroupArea;
25,361✔
895
    }
896
    public set showGroupArea(value: boolean) {
897
        this._showGroupArea = value;
1✔
898
        this.notifyChanges(true);
1✔
899
    }
900

901
    /**
902
     * @hidden @internal
903
     */
904
    public override isColumnGrouped(fieldName: string): boolean {
905
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
2!
906
    }
907

908
    /**
909
     * @hidden @internal
910
     */
911
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
912
        if (this.isDetailRecord(rowData)) {
149,662✔
913
            const cachedData = this.childDetailTemplates.get(rowData.detailsData);
2,383✔
914
            const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : rowData.detailsData;
2,383✔
915
            if (cachedData) {
2,383✔
916
                const view = cachedData.view;
2,182✔
917
                const tmlpOutlet = cachedData.owner;
2,182✔
918
                return {
2,182✔
919
                    $implicit: rowData.detailsData,
920
                    moveView: view,
921
                    owner: tmlpOutlet,
922
                    index: this.dataView.indexOf(rowData),
923
                    templateID: {
924
                        type: 'detailRow',
925
                        id: rowID
926
                    }
927
                };
928
            } else {
929
                // child rows contain unique grids, hence should have unique templates
930
                return {
201✔
931
                    $implicit: rowData.detailsData,
932
                    templateID: {
933
                        type: 'detailRow',
934
                        id: rowID
935
                    },
936
                    index: this.dataView.indexOf(rowData)
937
                };
938
            }
939
        }
940
        return {
147,279✔
941
            $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
147,279✔
942
            index: this.getDataViewIndex(rowIndex, pinned),
943
            templateID: {
944
                type: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
285,369✔
945
                id: null
946
            },
947
            disabled: this.isGhostRecord(rowData)
948
        };
949
    }
950

951
    /**
952
     * @hidden @internal
953
     */
954
    public viewCreatedHandler(args) {
955
        if (args.context.templateID.type === 'detailRow') {
19,128✔
956
            this.childDetailTemplates.set(args.context.$implicit, args);
201✔
957
        }
958
    }
959

960
    /**
961
     * @hidden @internal
962
     */
963
    public viewMovedHandler(args) {
964
        if (args.context.templateID.type === 'detailRow') {
64✔
965
            // view was moved, update owner in cache
966
            const key = args.context.$implicit;
64✔
967
            const cachedData = this.childDetailTemplates.get(key);
64✔
968
            cachedData.owner = args.owner;
64✔
969
        }
970
    }
971

972
    /**
973
     * @hidden @internal
974
     */
975
    public get iconTemplate() {
976
        if (this.groupsExpanded) {
2,181✔
977
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
2,123✔
978
        } else {
979
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
58✔
980
        }
981
    }
982

983
    /**
984
     * @hidden @internal
985
     */
986
    public override ngAfterContentInit() {
987
        super.ngAfterContentInit();
1,994✔
988
        if (this.allowFiltering && this.hasColumnLayouts) {
1,994✔
989
            this.filterMode = FilterMode.excelStyleFilter;
2✔
990
        }
991
        if (this.groupTemplate) {
1,994✔
992
            this._groupRowTemplate = this.groupTemplate.template;
2✔
993
        }
994

995
        if (this.detailTemplateDirective) {
1,994✔
996
            this._detailTemplate = this.detailTemplateDirective;
60✔
997
        }
998

999

1000
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
1,994✔
1001
            this._setGroupColsVisibility(this.hideGroupedColumns);
11✔
1002
        }
1003
        this._setupNavigationService();
1,994✔
1004
    }
1005

1006
    /**
1007
     * @hidden @internal
1008
     */
1009
    public override ngAfterViewInit() {
1010
        super.ngAfterViewInit();
2,007✔
1011
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
2,007✔
1012
            const rowData = view.context.$implicit;
2,690✔
1013
            if (this.isDetailRecord(rowData)) {
2,690✔
1014
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
6✔
1015
                if (cachedData) {
6✔
1016
                    const tmlpOutlet = cachedData.owner;
6✔
1017
                    tmlpOutlet._viewContainerRef.detach(0);
6✔
1018
                }
1019
            }
1020
        });
1021

1022
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
2,007✔
1023
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
375✔
1024
                return;
139✔
1025
            }
1026

1027
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
236✔
1028
                const fieldName = sortExpr.fieldName;
13✔
1029
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
15✔
1030
                if (groupingExpr) {
13✔
1031
                    groupingExpr.dir = sortExpr.dir;
7✔
1032
                }
1033
            });
1034
        });
1035
    }
1036

1037
    /**
1038
     * @hidden @internal
1039
     */
1040
    public override ngOnInit() {
1041
        super.ngOnInit();
1,994✔
1042
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
2,415,587✔
1043
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
1,994✔
1044
            this.crudService.endEdit(false);
263✔
1045
            this.summaryService.updateSummaryCache(args);
263✔
1046
            this._headerFeaturesWidth = NaN;
263✔
1047
        });
1048
    }
1049

1050
    /**
1051
     * @hidden @internal
1052
     */
1053
    public override ngDoCheck(): void {
1054
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
8,639✔
1055
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
37✔
1056
            if (changes && this._columns.length > 0) {
37✔
1057
                changes.forEachAddedItem((rec) => {
14✔
1058
                    const col = this.getColumnByName(rec.item.fieldName);
21✔
1059
                    if (col) {
21✔
1060
                        col.hidden = true;
20✔
1061
                    }
1062
                });
1063
                changes.forEachRemovedItem((rec) => {
14✔
UNCOV
1064
                    const col = this.getColumnByName(rec.item.fieldName);
×
UNCOV
1065
                    col.hidden = false;
×
1066
                });
1067
            }
1068
        }
1069
        super.ngDoCheck();
8,639✔
1070
    }
1071

1072
    /**
1073
     * @hidden @internal
1074
     */
1075
    public dataLoading(event) {
1076
        this.dataPreLoad.emit(event);
131✔
1077
    }
1078

1079
    /**
1080
     *
1081
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
1082
     *
1083
     * @remarks
1084
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
1085
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
1086
     */
1087
    public override getSelectedData(formatters = false, headers = false): any[] {
334✔
1088
        if (this.groupingExpressions.length || this.hasDetails) {
180✔
1089
            const source = [];
15✔
1090

1091
            const process = (record) => {
15✔
1092
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
220✔
1093
                    source.push(null);
97✔
1094
                    return;
97✔
1095
                }
1096
                source.push(record);
123✔
1097

1098
            };
1099

1100
            this.dataView.forEach(process);
15✔
1101
            return this.extractDataFromSelection(source, formatters, headers);
15✔
1102
        } else {
1103
            return super.getSelectedData(formatters, headers);
165✔
1104
        }
1105
    }
1106

1107
    /**
1108
     * Returns the `IgxGridRow` by index.
1109
     *
1110
     * @example
1111
     * ```typescript
1112
     * const myRow = grid.getRowByIndex(1);
1113
     * ```
1114
     * @param index
1115
     */
1116
    public getRowByIndex(index: number): RowType {
1117
        let row: RowType;
1118
        if (index < 0) {
593!
UNCOV
1119
            return undefined;
×
1120
        }
1121
        if (this.dataView.length >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) {
593!
1122
            row = this.createRow(index);
593✔
1123
        } else {
UNCOV
1124
            if (!(index < this.virtualizationState.startIndex) && !(index > this.virtualizationState.startIndex + this.virtualizationState.chunkSize)) {
×
UNCOV
1125
                row = this.createRow(index);
×
1126
            }
1127
        }
1128

1129
        if (this.pagingMode === 1 && this.page !== 0) {
593✔
1130
            row.index = index + this.perPage * this.page;
2✔
1131
        }
1132
        return row;
593✔
1133
    }
1134

1135
    /**
1136
     * Returns `IgxGridRow` object by the specified primary key.
1137
     *
1138
     * @remarks
1139
     * Requires that the `primaryKey` property is set.
1140
     * @example
1141
     * ```typescript
1142
     * const myRow = this.grid1.getRowByKey("cell5");
1143
     * ```
1144
     * @param keyValue
1145
     */
1146
    public getRowByKey(key: any): RowType {
1147
        const rec = this.filteredSortedData ? this.primaryKey ?
161✔
1148
            this.filteredSortedData.find(record => record[this.primaryKey] === key) :
287✔
1149
            this.filteredSortedData.find(record => record === key) : undefined;
222✔
1150
        const index = this.dataView.indexOf(rec);
161✔
1151
        if (index < 0 || index > this.dataView.length) {
161✔
1152
            return undefined;
10✔
1153
        }
1154

1155
        return new IgxGridRow(this, index, rec);
151✔
1156
    }
1157

1158
    /**
1159
     * @hidden @internal
1160
     */
1161
    public allRows(): RowType[] {
1162
        return this.dataView.map((rec, index) => {
78✔
1163
            this.pagingMode === 1 && this.page !== 0 ? index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
3,909!
1164
            return this.createRow(index);
3,909✔
1165
        });
1166
    }
1167

1168
    /**
1169
     * Returns the collection of `IgxGridRow`s for current page.
1170
     *
1171
     * @hidden @internal
1172
     */
1173
    public dataRows(): RowType[] {
1174
        return this.allRows().filter(row => row instanceof IgxGridRow);
3,909✔
1175
    }
1176

1177
    /**
1178
     * Returns an array of the selected `IgxGridCell`s.
1179
     *
1180
     * @example
1181
     * ```typescript
1182
     * const selectedCells = this.grid.selectedCells;
1183
     * ```
1184
     */
1185
    public get selectedCells(): CellType[] {
1186
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
34,455✔
1187
            .reduce((a, b) => a.concat(b), []);
2,213✔
1188
    }
1189

1190
    /**
1191
     * Returns a `CellType` object that matches the conditions.
1192
     *
1193
     * @example
1194
     * ```typescript
1195
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
1196
     * ```
1197
     * @param rowIndex
1198
     * @param columnField
1199
     */
1200
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
1201
        const row = this.getRowByIndex(rowIndex);
456✔
1202
        const column = this._columns.find((col) => col.field === columnField);
1,399✔
1203
        if (row && row instanceof IgxGridRow && !row.data?.detailsData && column) {
456✔
1204
            if (this.pagingMode === 1 && this.page !== 0) {
456!
UNCOV
1205
                row.index = rowIndex + this.perPage * this.page;
×
1206
            }
1207
            return new IgxGridCell(this, row.index, column);
456✔
1208
        }
1209
    }
1210

1211
    /**
1212
     * Returns a `CellType` object that matches the conditions.
1213
     *
1214
     * @remarks
1215
     * Requires that the primaryKey property is set.
1216
     * @example
1217
     * ```typescript
1218
     * grid.getCellByKey(1, 'index');
1219
     * ```
1220
     * @param rowSelector match any rowID
1221
     * @param columnField
1222
     */
1223
    public getCellByKey(rowSelector: any, columnField: string): CellType {
1224
        const row = this.getRowByKey(rowSelector);
15✔
1225
        const column = this._columns.find((col) => col.field === columnField);
39✔
1226
        if (row && column) {
15✔
1227
            return new IgxGridCell(this, row.index, column);
15✔
1228
        }
1229
    }
1230

1231
    public override pinRow(rowID: any, index?: number): boolean {
1232
        const row = this.getRowByKey(rowID);
89✔
1233
        return super.pinRow(rowID, index, row);
89✔
1234
    }
1235

1236
    public override unpinRow(rowID: any): boolean {
1237
        const row = this.getRowByKey(rowID);
18✔
1238
        return super.unpinRow(rowID, row);
18✔
1239
    }
1240

1241
    /**
1242
     * @hidden @internal
1243
     */
1244
    public createRow(index: number, data?: any): RowType {
1245
        let row: RowType;
1246

1247
        const dataIndex = this._getDataViewIndex(index);
43,054✔
1248
        const rec = data ?? this.dataView[dataIndex];
43,054✔
1249

1250
        if (rec && this.isGroupByRecord(rec)) {
43,054✔
1251
            row = new IgxGroupByRow(this, index, rec);
127✔
1252
        }
1253
        if (rec && this.isSummaryRow(rec)) {
43,054✔
1254
            row = new IgxSummaryRow(this, index, rec.summaries);
20✔
1255
        }
1256
        // if found record is a no a groupby or summary row, return IgxGridRow instance
1257
        if (!row && rec) {
43,054✔
1258
            row = new IgxGridRow(this, index, rec);
41,334✔
1259
        }
1260

1261
        return row;
43,054✔
1262
    }
1263

1264
    /**
1265
     * @hidden @internal
1266
     */
1267
    protected override get defaultTargetBodyHeight(): number {
1268
        const allItems = this.totalItemCount || this.dataLength;
178✔
1269
        return this.renderedActualRowHeight * Math.min(this._defaultTargetRecordNumber,
178✔
1270
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
178✔
1271
    }
1272

1273
    /**
1274
     * @hidden @internal
1275
     */
1276
    protected override getGroupAreaHeight(): number {
1277
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
5,321✔
1278
    }
1279

1280
    /**
1281
     * @hidden @internal
1282
     */
1283
    protected override onColumnsAddedOrRemoved() {
1284
        // update grouping states
1285
        this.groupablePipeTrigger++;
62✔
1286
        if (this.groupingExpressions && this.hideGroupedColumns) {
62✔
1287
            this._setGroupColsVisibility(this.hideGroupedColumns);
2✔
1288
        }
1289
        super.onColumnsAddedOrRemoved();
62✔
1290
    }
1291

1292
    /**
1293
     * @hidden @internal
1294
     */
1295
    protected override scrollTo(row: any | number, column: any | number): void {
1296
        if (this.groupingExpressions && this.groupingExpressions.length
103✔
1297
            && typeof (row) !== 'number') {
1298
            const rowIndex = this.groupingResult.indexOf(row);
31✔
1299
            const groupByRecord = this.groupingMetadata[rowIndex];
31✔
1300
            if (groupByRecord) {
31✔
1301
                this._fullyExpandGroup(groupByRecord);
31✔
1302
            }
1303
        }
1304

1305
        super.scrollTo(row, column, this.groupingFlatResult);
103✔
1306
    }
1307

1308
    /**
1309
     * @hidden @internal
1310
     */
1311
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
1312
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
13,293✔
1313
    }
1314

1315
    /**
1316
     * @hidden
1317
     */
1318
    protected _toggleGroup(groupRow: IGroupByRecord) {
1319
        this._gridAPI.groupBy_toggle_group(groupRow);
58✔
1320
    }
1321

1322
    /**
1323
     * @hidden @internal
1324
     */
1325
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
1326
        this._gridAPI.groupBy_fully_expand_group(groupRow);
31✔
1327
    }
1328

1329
    /**
1330
     * @hidden @internal
1331
     */
1332
    protected _applyGrouping() {
1333
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
399✔
1334
    }
1335

1336
    private _setupNavigationService() {
1337
        if (this.hasColumnLayouts) {
1,994✔
1338
            this.navigation = new IgxGridMRLNavigationService(this.platform);
119✔
1339
            this.navigation.grid = this;
119✔
1340
        }
1341
    }
1342

1343
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
1344
        if (expression instanceof Array) {
231✔
1345
            for (const singleExpression of expression) {
21✔
1346
                if (!singleExpression.fieldName) {
54✔
1347
                    return true;
1✔
1348
                }
1349
            }
1350
            return false;
20✔
1351
        }
1352
        return !expression.fieldName;
210✔
1353
    }
1354

1355
    private _setGroupColsVisibility(value) {
1356
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
49✔
1357
            this.groupingExpressions.forEach((expr) => {
17✔
1358
                const col = this.getColumnByName(expr.fieldName);
6✔
1359
                col.hidden = value;
6✔
1360
            });
1361
        }
1362
    }
1363

1364
    private stringifyCallback(key: string, val: any) {
1365
        // Workaround for Blazor, since its wrappers inject this externalObject that cannot serialize.
1366
        if (key === 'externalObject') {
2,579!
UNCOV
1367
            return undefined;
×
1368
        }
1369
        return val;
2,579✔
1370
    }
1371
}
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