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

IgniteUI / igniteui-angular / 6655736531

26 Oct 2023 02:38PM UTC coverage: 92.269% (+0.005%) from 92.264%
6655736531

push

github

web-flow
Merge pull request #13588 from IgniteUI/mkirkova/fix-13514-15.1.x

Return updated values when groupingDone is emitted

15313 of 17987 branches covered (0.0%)

26854 of 29104 relevant lines covered (92.27%)

29738.96 hits per line

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

93.52
/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
5
} from '@angular/core';
6
import { IgxGridBaseDirective } from '../grid-base.directive';
7
import { IgxGridNavigationService } from '../grid-navigation.service';
8
import { IgxGridAPIService } from './grid-api.service';
9
import { cloneArray, IBaseEventArgs } from '../../core/utils';
10
import { IGroupByRecord } from '../../data-operations/groupby-record.interface';
11
import { IgxGroupByRowTemplateDirective, IgxGridDetailTemplateDirective } from './grid.directives';
12
import { IgxGridGroupByRowComponent } from './groupby-row.component';
13
import { IGroupByExpandState } from '../../data-operations/groupby-expand-state.interface';
14
import { IForOfState } from '../../directives/for-of/for_of.directive';
15
import { IgxColumnComponent } from '../columns/column.component';
16
import { take, takeUntil } from 'rxjs/operators';
17
import { IgxFilteringService } from '../filtering/grid-filtering.service';
18
import { IGroupingExpression } from '../../data-operations/grouping-expression.interface';
19
import { IgxColumnResizingService } from '../resizing/resizing.service';
20
import { IgxGridSummaryService } from '../summaries/grid-summary.service';
21
import { IgxGridSelectionService } from '../selection/selection.service';
22
import { IgxForOfSyncService, IgxForOfScrollSyncService } from '../../directives/for-of/for_of.sync.service';
23
import { IgxGridMRLNavigationService } from '../grid-mrl-navigation.service';
24
import { FilterMode, GridInstanceType } from '../common/enums';
25
import { CellType, GridType, IgxGridMasterDetailContext, IgxGroupByRowSelectorTemplateContext, IgxGroupByRowTemplateContext, IGX_GRID_BASE, IGX_GRID_SERVICE_BASE, RowType } from '../common/grid.interface';
26
import { IgxGroupByRowSelectorDirective } from '../selection/row-selectors';
2✔
27
import { IgxGridCRUDService } from '../common/crud.service';
28
import { IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../grid-public-row';
29
import { IgxGridCell } from '../grid-public-cell';
30
import { ISortingExpression } from '../../data-operations/sorting-strategy';
31
import { IGridGroupingStrategy } from '../common/strategy';
32
import { IgxGridValidationService } from './grid-validation.service';
33
import { Observable, Subject } from 'rxjs';
34

35
let NEXT_ID = 0;
36

37
export interface IGroupingDoneEventArgs extends IBaseEventArgs {
38
    expressions: Array<ISortingExpression> | ISortingExpression;
39
    groupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
40
    ungroupedColumns: Array<IgxColumnComponent> | IgxColumnComponent;
41
}
42

43
/**
44
 * Grid provides a way to present and manipulate tabular data.
45
 *
46
 * @igxModule IgxGridModule
2✔
47
 * @igxGroup Grids & Lists
48
 * @igxKeywords grid, table
1,955✔
49
 * @igxTheme igx-grid-theme
1,955✔
50
 * @remarks
1,955✔
51
 * The Ignite UI Grid is used for presenting and manipulating tabular data in the simplest way possible.  Once data
1,955✔
52
 * has been bound, it can be manipulated through filtering, sorting & editing operations.
1,955✔
53
 * @example
1,955✔
54
 * ```html
1,955✔
55
 * <igx-grid [data]="employeeData" [autoGenerate]="false">
1,955✔
56
 *   <igx-column field="first" header="First Name"></igx-column>
1,955✔
57
 *   <igx-column field="last" header="Last Name"></igx-column>
58
 *   <igx-column field="role" header="Role"></igx-column>
59
 * </igx-grid>
60
 * ```
1,955✔
61
 */
62
@Component({
63
    changeDetection: ChangeDetectionStrategy.OnPush,
64
    preserveWhitespaces: false,
1,955✔
65
    providers: [
1,955✔
66
        IgxGridCRUDService,
1,955✔
67
        IgxGridNavigationService,
1,955✔
68
        IgxGridSummaryService,
1,955✔
69
        IgxGridSelectionService,
70
        IgxGridValidationService,
71
        { provide: IGX_GRID_SERVICE_BASE, useClass: IgxGridAPIService },
72
        { provide: IGX_GRID_BASE, useExisting: IgxGridComponent },
1,955✔
73
        IgxFilteringService,
74
        IgxColumnResizingService,
75
        IgxForOfSyncService,
76
        IgxForOfScrollSyncService
1,955✔
77
    ],
78
    selector: 'igx-grid',
79
    templateUrl: './grid.component.html'
223,434✔
80
})
81
export class IgxGridComponent extends IgxGridBaseDirective implements GridType, OnInit, DoCheck, AfterContentInit, AfterViewInit {
82
    /**
83
     * Emitted when a new chunk of data is loaded from virtualization.
84
     *
85
     * @example
86
     * ```typescript
87
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataPreLoad)='handleDataPreloadEvent()'></igx-grid>
88
     * ```
89
     */
90
    @Output()
91
    public dataPreLoad = new EventEmitter<IForOfState>();
92

93
    /**
94
     * @hidden
95
     */
96
    @Output()
97
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
98

99
    /**
100
     * @hidden @internal
1✔
101
     */
102
    @Output()
103
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
104

105
    /**
106
     * Emitted when columns are grouped/ungrouped.
107
     *
108
     * @remarks
109
     * The `groupingDone` event would be raised only once if several columns get grouped at once by calling
110
     * the `groupBy()` or `clearGrouping()` API methods and passing an array as an argument.
111
     * The event arguments provide the `expressions`, `groupedColumns` and `ungroupedColumns` properties, which contain
20,387✔
112
     * the `ISortingExpression` and the `IgxColumnComponent` related to the grouping/ungrouping operation.
113
     * Please note that `groupedColumns` and `ungroupedColumns` show only the **newly** changed columns (affected by the **last**
114
     * grouping/ungrouping operation), not all columns which are currently grouped/ungrouped.
99,920✔
115
     * columns.
116
     * @example
117
     * ```html
2,032✔
118
     * <igx-grid #grid [data]="localData" (groupingDone)="groupingDone($event)" [autoGenerate]="true"></igx-grid>
2,032✔
119
     * ```
2,032✔
120
     */
2,032✔
121
    @Output()
91✔
122
    public groupingDone = new EventEmitter<IGroupingDoneEventArgs>();
123

2,032✔
124
    /**
18✔
125
     * Gets/Sets whether created groups are rendered expanded or collapsed.
126
     *
2,032✔
127
     * @remarks
2,032✔
128
     * The default rendered state is expanded.
890✔
129
     * @example
130
     * ```html
131
     * <igx-grid #grid [data]="Data" [groupsExpanded]="false" [autoGenerate]="true"></igx-grid>
2,032✔
132
     * ```
1✔
133
     */
134
    @Input()
135
    public groupsExpanded = true;
136

137
    /**
138
     * Gets/Sets the template that will be rendered as a GroupBy drop area.
139
     *
140
     * @remarks
141
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
142
     * @example
143
     * ```html
144
     * <igx-grid [dropAreaTemplate]="dropAreaRef">
145
     * </igx-grid>
146
     * <ng-template #myDropArea>
147
     *      <span> Custom drop area! </span>
4✔
148
     * </ng-template>
4✔
149
     * ```
150
     */
151
    @Input()
1,272✔
152
    public dropAreaTemplate: TemplateRef<void>;
153

154
    /**
12,140✔
155
     * @hidden @internal
156
     */
157
    @ContentChild(IgxGridDetailTemplateDirective, { read: TemplateRef })
1,351,155✔
158
    public detailTemplateDirective: TemplateRef<IgxGridMasterDetailContext>;
159

160

599✔
161
    /**
114✔
162
     * Returns a reference to the master-detail template.
163
     * ```typescript
485✔
164
     * let detailTemplate = this.grid.detailTemplate;
1✔
165
     * ```
166
     *
484✔
167
     * @memberof IgxColumnComponent
484✔
168
     */
484✔
169
    @Input('detailTemplate')
484✔
170
    public get detailTemplate(): TemplateRef<IgxGridMasterDetailContext> {
484✔
171
        return this._detailTemplate;
172
    }
389✔
173
    /**
389✔
174
     * Sets the master-detail template.
175
     * ```html
484✔
176
     * <ng-template #detailTemplate igxGridDetail let-dataItem>
262✔
177
     *    <div>
262✔
178
     *       <div><span class='categoryStyle'>City:</span> {{dataItem.City}}</div>
314✔
179
     *       <div><span class='categoryStyle'>Address:</span> {{dataItem.Address}}</div>
262✔
180
     *    </div>
263✔
181
     * </ng-template>
182
     * ```
262✔
183
     * ```typescript
262✔
184
     * @ViewChild("'detailTemplate'", {read: TemplateRef })
26✔
185
     * public detailTemplate: TemplateRef<any>;
186
     * this.grid.detailTemplate = this.detailTemplate;
262✔
187
     * ```
262✔
188
     *
189
     * @memberof IgxColumnComponent
190
     */
191
    public set detailTemplate(template: TemplateRef<IgxGridMasterDetailContext>) {
192
        this._detailTemplate = template;
262✔
193
    }
257✔
194

195
    /**
196
     * @hidden @internal
197
     */
198
    @HostBinding('attr.role')
32,107✔
199
    public role = 'grid';
200

201
    /**
80!
202
     * Gets/Sets the value of the `id` attribute.
80✔
203
     *
204
     * @remarks
80✔
205
     * If not provided it will be automatically generated.
80!
206
     * @example
80✔
207
     * ```html
208
     * <igx-grid [id]="'igx-grid-1'" [data]="Data" [autoGenerate]="true"></igx-grid>
209
     * ```
210
     */
2,033✔
211
    @HostBinding('attr.id')
212
    @Input()
213
    public id = `igx-grid-${NEXT_ID++}`;
34✔
214

15✔
215
    /**
216
     * @hidden @internal
217
     */
19✔
218
    @ViewChild('record_template', { read: TemplateRef, static: true })
219
    protected recordTemplate: TemplateRef<any>;
34!
220

34✔
221
    @ViewChild('detail_template_container', { read: TemplateRef, static: true })
222
    protected detailTemplateContainer: TemplateRef<any>;
34✔
223

224
    @ViewChild('group_template', { read: TemplateRef, static: true })
225
    protected defaultGroupTemplate: TemplateRef<any>;
20,364✔
226

227
    @ViewChild('summary_template', { read: TemplateRef, static: true })
228
    protected summaryTemplate: TemplateRef<any>;
13✔
229

230
    /**
231
     * @hidden @internal
1✔
232
     */
1✔
233
    @ContentChild(IgxGroupByRowTemplateDirective, { read: IgxGroupByRowTemplateDirective })
234
    protected groupTemplate: IgxGroupByRowTemplateDirective;
235

3,026✔
236
    /**
237
     * @hidden
238
     * @internal
239
     */
240
    @ContentChildren(IgxGroupByRowSelectorDirective, { read: TemplateRef, descendants: false })
241
    protected groupByRowSelectorsTemplates: QueryList<TemplateRef<IgxGroupByRowSelectorTemplateContext>>;
242

243
    @ViewChildren(IgxGridGroupByRowComponent, { read: IgxGridGroupByRowComponent })
244
    private _groupsRowList: QueryList<IgxGridGroupByRowComponent>;
245

246
    private _groupsRecords: IGroupByRecord[] = [];
247
    /**
248
     * Gets the hierarchical representation of the group by records.
249
     *
250
     * @example
×
251
     * ```typescript
×
252
     * let groupRecords = this.grid.groupsRecords;
×
253
     * ```
×
254
     */
255
    public get groupsRecords(): IGroupByRecord[] {
256
        return this._groupsRecords;
257
    }
258

259
    /**
260
     * @hidden @internal
261
     * Includes children of collapsed group rows.
262
     */
263
    public groupingResult: any[];
264

265
    /**
142✔
266
     * @hidden @internal
142!
267
     */
×
268
    public groupingMetadata: any[];
269

600✔
270
    /**
574✔
271
     * @hidden @internal
142✔
272
     * Does not include children of collapsed group rows.
142✔
273
     */
274
    public groupingFlatResult: any[];
275
    /**
373✔
276
     * @hidden
277
     */
278
    protected _groupingExpressions: IGroupingExpression[] = [];
279
    /**
280
     * @hidden
281
     */
282
    protected _groupingExpandState: IGroupByExpandState[] = [];
283
    /**
284
     * @hidden
285
     */
286
    protected _groupRowTemplate: TemplateRef<IgxGroupByRowTemplateContext>;
287
    /**
288
     * @hidden
289
     */
290
    protected _groupAreaTemplate: TemplateRef<any>;
291
    /**
1✔
292
     * @hidden
293
     */
294
    protected _groupStrategy: IGridGroupingStrategy;
295
    /**
296
     * @hidden
297
     */
1,519✔
298
    protected groupingDiffer;
299
    private _data?: any[] | null;
300
    private _hideGroupedColumns = false;
301
    private _dropAreaMessage = null;
302
    private _showGroupArea = true;
303

304
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
305
    private _detailTemplate;
306

2✔
307

308
    /**
309
     * Gets/Sets the array of data that populates the `IgxGridComponent`.
310
     *
311
     * @example
312
     * ```html
221,915✔
313
     * <igx-grid [data]="Data" [autoGenerate]="true"></igx-grid>
314
     * ```
315
     */
316
    @Input()
317
    public get data(): any[] | null {
318
        return this._data;
108,581✔
319
    }
7,036✔
320

321
    public set data(value: any[] | null) {
101,545✔
322
        const dataLoaded = (!this._data || this._data.length === 0) && value && value.length > 0;
1,324✔
323
        this._data = value || [];
324
        this.summaryService.clearSummaryCache();
100,221✔
325
        if (!this._init) {
1,519✔
326
            this.validation.updateAll(this._data);
327
        }
328

98,702✔
329
        if (this.shouldGenerate) {
330
            this.setupColumns();
331
        }
332
        this.cdr.markForCheck();
333
        if (this.isPercentHeight) {
334
            this.notifyChanges(true);
335
        }
119,859✔
336
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
337
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
338
            this.recalculateAutoSizes();
339
        }
340
    }
341

1,519!
342
    /**
343
     * Gets/Sets the total number of records in the data source.
344
     *
2,071✔
345
     * @remarks
346
     * This property is required for remote grid virtualization to function when it is bound to remote data.
347
     * @example
1✔
348
     * ```typescript
1✔
349
     * const itemCount = this.grid1.totalItemCount;
350
     * this.grid1.totalItemCount = 55;
351
     * ```
352
     */
353
    public set totalItemCount(count) {
354
        this.verticalScrollContainer.totalItemCount = count;
355
        this.cdr.detectChanges();
356
    }
357

358
    public get totalItemCount() {
359
        return this.verticalScrollContainer.totalItemCount;
360
    }
×
361

362
    private get _gridAPI(): IgxGridAPIService {
363
        return this.gridAPI as IgxGridAPIService;
×
364
    }
×
365

366
    private childDetailTemplates: Map<any, any> = new Map();
367

368
    /**
369
     * @hidden @internal
370
     */
371
    public groupingPerformedSubject = new Subject<void>();
372

373
    /**
374
     * @hidden @internal
375
     */
376
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
377

378
    /**
379
     * Gets/Sets the group by state.
380
     *
381
     * @example
382
     * ```typescript
383
     * let groupByState = this.grid.groupingExpressions;
225✔
384
     * this.grid.groupingExpressions = [...];
1✔
385
     * ```
386
     * @remarks
224✔
387
     * Supports two-way data binding.
224✔
388
     * @example
19✔
389
     * ```html
390
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpressions)]="model.groupingExpressions"></igx-grid>
391
     * ```
205✔
392
     */
393
    @Input()
223✔
394
    public get groupingExpressions(): IGroupingExpression[] {
395
        return this._groupingExpressions;
396
    }
397

398
    public set groupingExpressions(value: IGroupingExpression[]) {
399
        if (this.groupingExpressions === value) {
400
            return;
401
        }
402
        if (value && value.length > 10) {
403
            throw Error('Maximum amount of grouped columns is 10.');
404
        }
405
        const oldExpressions: IGroupingExpression[] = this.groupingExpressions;
406
        const newExpressions: IGroupingExpression[] = value;
407
        this._groupingExpressions = cloneArray(value);
408
        this.groupingExpressionsChange.emit(this._groupingExpressions);
409
        if (this._gridAPI.grid) {
410
            /* grouping and sorting are working separate from each other */
18✔
411
            this._applyGrouping();
18✔
412
            this.notifyChanges();
18✔
413
        }
18✔
414
        if (!this._init && JSON.stringify(oldExpressions) !== JSON.stringify(newExpressions) && this._columns) {
415
            const groupedCols: IgxColumnComponent[] = [];
416
            const ungroupedCols: IgxColumnComponent[] = [];
417
            const groupedColsArr = newExpressions.filter((obj) => !oldExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
418
            groupedColsArr.forEach((elem) => {
419
                groupedCols.push(this.getColumnByName(elem.fieldName));
420
            }, this);
421
            const ungroupedColsArr = oldExpressions.filter((obj) => !newExpressions.some((obj2) => obj.fieldName === obj2.fieldName));
422
            ungroupedColsArr.forEach((elem) => {
423
                ungroupedCols.push(this.getColumnByName(elem.fieldName));
424
            }, this);
425
            this.notifyChanges();
426
            const groupingDoneArgs: IGroupingDoneEventArgs = {
10,937✔
427
                expressions: newExpressions,
10,937✔
428
                groupedColumns: groupedCols,
429
                ungroupedColumns: ungroupedCols
430
            };
431
            this.groupingPerformed$.pipe(take(1)).subscribe(() => {
432
                this.groupingDone.emit(groupingDoneArgs);
433
            });
434
        }
435
    }
436

437
    /**
438
     * Gets/Sets a list of expansion states for group rows.
439
     *
440
     * @remarks
51✔
441
     * Includes only states that differ from the default one (controlled through groupsExpanded and states that the user has changed.
51✔
442
     * Contains the expansion state (expanded: boolean) and the unique identifier for the group row (Array).
443
     * Supports two-way data binding.
444
     * @example
445
     * ```html
446
     * <igx-grid #grid [data]="Data" [autoGenerate]="true" [(groupingExpansionState)]="model.groupingExpansionState"></igx-grid>
447
     * ```
448
     */
449
    @Input()
450
    public get groupingExpansionState() {
451
        return this._groupingExpandState;
452
    }
453

454
    public set groupingExpansionState(value) {
4✔
455
        if (value !== this._groupingExpandState) {
4✔
456
            this.groupingExpansionStateChange.emit(value);
457
        }
458
        this._groupingExpandState = value;
459
        if (this.gridAPI.grid) {
460
            this.cdr.detectChanges();
461
        }
462
    }
463

464
    /**
465
     * Gets/Sets whether the grouped columns should be hidden.
466
     *
467
     * @remarks
468
     * The default value is "false"
2✔
469
     * @example
2✔
470
     * ```html
471
     * <igx-grid #grid [data]="localData" [hideGroupedColumns]="true" [autoGenerate]="true"></igx-grid>
472
     * ```
473
     */
474
    @Input()
475
    public get hideGroupedColumns() {
476
        return this._hideGroupedColumns;
477
    }
478

479
    public set hideGroupedColumns(value: boolean) {
480
        if (value) {
481
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
482
        } else {
×
483
            this.groupingDiffer = null;
×
484
        }
485
        if (this._columns && this.groupingExpressions) {
486
            this._setGroupColsVisibility(value);
487
        }
488

489
        this._hideGroupedColumns = value;
490
    }
277,347✔
491

492
    /**
493
     * Gets/Sets the grouping strategy of the grid.
494
     *
495
     * @remarks The default IgxGrouping extends from IgxSorting and a custom one can be used as a `sortStrategy` as well.
496
     *
497
     * @example
498
     * ```html
499
     *  <igx-grid #grid [data]="localData" [groupStrategy]="groupStrategy"></igx-grid>
500
     * ```
501
     */
502
    @Input()
11✔
503
    public get groupStrategy(): IGridGroupingStrategy {
11✔
504
        return this._groupStrategy;
11✔
505
    }
506

507
    public set groupStrategy(value: IGridGroupingStrategy) {
508
        this._groupStrategy = value;
509
    }
510

511
    /**
512
     * Gets/Sets the message displayed inside the GroupBy drop area where columns can be dragged on.
513
     *
514
     * @remarks
515
     * The grid needs to have at least one groupable column in order the GroupBy area to be displayed.
129,599✔
516
     * @example
517
     * ```html
518
     * <igx-grid dropAreaMessage="Drop here to group!">
20,362✔
519
     *      <igx-column [groupable]="true" field="ID"></igx-column>
520
     * </igx-grid>
521
     * ```
×
522
     */
×
523
    @Input()
524
    public set dropAreaMessage(value: string) {
525
        this._dropAreaMessage = value;
526
        this.notifyChanges();
527
    }
528

529
    public get dropAreaMessage(): string {
530
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
531
    }
532

533
    /**
×
534
     * @deprecated in version 12.1.0. Use `getCellByColumn` or `getCellByKey` instead
535
     *
536
     * Returns a `CellType` object that matches the conditions.
537
     *
538
     * @example
539
     * ```typescript
2!
540
     * const myCell = this.grid1.getCellByColumnVisibleIndex(2,"UnitPrice");
541
     * ```
542
     * @param rowIndex
543
     * @param index
544
     */
545
     public getCellByColumnVisibleIndex(rowIndex: number, index: number): CellType {
109,175✔
546
        const row = this.getRowByIndex(rowIndex);
1,519✔
547
        const column = this._columns.find((col) => col.visibleIndex === index);
1,519✔
548
        if (row && row instanceof IgxGridRow && !row.data?.detailsData && column) {
1,519✔
549
            return new IgxGridCell(this, rowIndex, column);
1,318✔
550
        }
1,318✔
551
    }
1,318✔
552

553
    /**
554
     * Gets the list of group rows.
555
     *
556
     * @example
557
     * ```typescript
558
     * const groupList = this.grid.groupsRowList;
559
     * ```
560
     */
561
    public get groupsRowList() {
562
        const res = new QueryList<any>();
563
        if (!this._groupsRowList) {
564
            return res;
201✔
565
        }
566
        const rList = this._groupsRowList.filter(item => item.element.nativeElement.parentElement !== null)
567
            .sort((item1, item2) => item1.index - item2.index);
568
        res.reset(rList);
569
        return res;
570
    }
571

572
    /**
573
     * Gets the group by row selector template.
574
     */
107,656✔
575
    @Input()
107,656✔
576
    public get groupByRowSelectorTemplate(): TemplateRef <IgxGroupByRowSelectorTemplateContext> {
577
        return this._groupByRowSelectorTemplate || this.groupByRowSelectorsTemplates?.first;
578
    }
208,276✔
579

580
    /**
581
     * Sets the group by row selector template.
582
     * ```html
583
     * <ng-template #template igxGroupByRowSelector let-groupByRowContext>
584
     * {{ groupByRowContext.selectedCount }} / {{ groupByRowContext.totalCount  }}
585
     * </ng-template>
586
     * ```
587
     * ```typescript
588
     * @ViewChild("'template'", {read: TemplateRef })
18,642✔
589
     * public template: TemplateRef<any>;
201✔
590
     * this.grid.groupByRowSelectorTemplate = this.template;
591
     * ```
592
     */
593
    public set groupByRowSelectorTemplate(template: TemplateRef<IgxGroupByRowSelectorTemplateContext>) {
594
        this._groupByRowSelectorTemplate = template;
595
    }
596

64!
597
    /**
598
     * @hidden @internal
64✔
599
     */
64✔
600
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
64✔
601
        return {
602
            $implicit: rowData,
603
            index
604
        };
605
    }
606

607
    /**
1,757✔
608
     * @hidden @internal
1,713✔
609
     */
610
    public detailsViewFocused(container, rowIndex) {
611
        this.navigation.setActiveNode({ row: rowIndex });
44✔
612
    }
613

614
    /**
615
     * @hidden @internal
616
     */
617
    public override get hasDetails() {
618
        return !!this.detailTemplate;
1,955✔
619
    }
1,955✔
620

2✔
621
    /**
622
     * @hidden @internal
1,955✔
623
     */
2✔
624
    public getRowTemplate(rowData) {
625
        if (this.isGroupByRecord(rowData)) {
1,955✔
626
            return this.defaultGroupTemplate;
60✔
627
        } else if (this.isSummaryRow(rowData)) {
628
            return this.summaryTemplate;
1,955✔
629
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
10✔
630
            return this.detailTemplateContainer;
631
        } else {
1,955✔
632
            return this.recordTemplate;
633
        }
634
    }
635

636
    /**
637
     * @hidden @internal
1,968✔
638
     */
1,968✔
639
    public override isDetailRecord(record) {
2,753✔
640
        return record && record.detailsData !== undefined;
2,753✔
641
    }
8✔
642

8!
643
    /**
8✔
644
     * @hidden @internal
8✔
645
     */
646
    public isDetailActive(rowIndex) {
647
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
648
    }
1,968✔
649

364✔
650
    /**
134✔
651
     * Gets/Sets the template reference for the group row.
652
     *
230✔
653
     * @example
13✔
654
     * ```
15✔
655
     * const groupRowTemplate = this.grid.groupRowTemplate;
13✔
656
     * this.grid.groupRowTemplate = myRowTemplate;
7✔
657
     * ```
658
     */
659
    @Input()
660
    public get groupRowTemplate(): TemplateRef<IgxGroupByRowTemplateContext> {
661
        return this._groupRowTemplate;
662
    }
663

664
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
665
        this._groupRowTemplate = template;
1,955✔
666
        this.notifyChanges();
1,504,018✔
667
    }
1,955✔
668

257✔
669

257✔
670
    /**
257✔
671
     * Gets/Sets the template reference of the `IgxGridComponent`'s group area.
672
     *
673
     * @example
674
     * ```typescript
675
     * const groupAreaTemplate = this.grid.groupAreaTemplate;
676
     * this.grid.groupAreaTemplate = myAreaTemplate.
677
     * ```
8,588✔
678
     */
36✔
679
    public get groupAreaTemplate(): TemplateRef<any> {
36✔
680
        return this._groupAreaTemplate;
14✔
681
    }
21✔
682

21✔
683
    public set groupAreaTemplate(template: TemplateRef<any>) {
20✔
684
        this._groupAreaTemplate = template;
685
        this.notifyChanges();
686
    }
14✔
687

×
688
    /** @hidden @internal */
×
689
    public trackChanges: (index, rec) => any;
690

691
    /**
692
     * Groups by a new `IgxColumnComponent` based on the provided expression, or modifies an existing one.
8,588✔
693
     *
694
     * @remarks
695
     * Also allows for multiple columns to be grouped at once if an array of `ISortingExpression` is passed.
696
     * The `groupingDone` event would get raised only **once** if this method gets called multiple times with the same arguments.
697
     * @example
698
     * ```typescript
128✔
699
     * this.grid.groupBy({ fieldName: name, dir: SortingDirection.Asc, ignoreCase: false });
700
     * this.grid.groupBy([
701
     *     { fieldName: name1, dir: SortingDirection.Asc, ignoreCase: false },
702
     *     { fieldName: name2, dir: SortingDirection.Desc, ignoreCase: true },
703
     *     { fieldName: name3, dir: SortingDirection.Desc, ignoreCase: false }
704
     * ]);
705
     * ```
706
     */
707
    public groupBy(expression: IGroupingExpression | Array<IGroupingExpression>): void {
708
        if (this.checkIfNoColumnField(expression)) {
334✔
709
            return;
180✔
710
        }
15✔
711
        this.crudService.endEdit(false);
15✔
712
        if (expression instanceof Array) {
220✔
713
            this._gridAPI.groupBy_multiple(expression);
97✔
714
        } else {
97✔
715
            this._gridAPI.groupBy(expression);
716
        }
123✔
717
        this.notifyChanges(true);
718
    }
15✔
719

15✔
720
    /**
721
     * Clears grouping for particular column, array of columns or all columns.
722
     *
165✔
723
     * @remarks
724
     * Clears all grouping in the grid, if no parameter is passed.
725
     * If a parameter is provided, clears grouping for a particular column or an array of columns.
726
     * @example
727
     * ```typescript
728
     * this.grid.clearGrouping(); //clears all grouping
729
     * this.grid.clearGrouping("ID"); //ungroups a single column
730
     * this.grid.clearGrouping(["ID", "Column1", "Column2"]); //ungroups multiple columns
731
     * ```
732
     * @param name Name of column or array of column names to be ungrouped.
733
     */
734
    public clearGrouping(name?: string | Array<string>): void {
735
        this._gridAPI.clear_groupby(name);
736
        this.calculateGridSizes();
581!
737
        this.notifyChanges(true);
×
738
        this.groupingPerformedSubject.next();
739
    }
581!
740

581✔
741
    /**
742
     * Returns if a group is expanded or not.
743
     *
×
744
     * @param group The group record.
×
745
     * @example
746
     * ```typescript
747
     * public groupRow: IGroupByRecord;
581✔
748
     * const expandedGroup = this.grid.isExpandedGroup(this.groupRow);
2✔
749
     * ```
750
     */
581✔
751
    public override isExpandedGroup(group: IGroupByRecord): boolean {
752
        const state: IGroupByExpandState = this._getStateForGroupRow(group);
753
        return state ? state.expanded : this.groupsExpanded;
754
    }
755

756
    /**
757
     * Toggles the expansion state of a group.
758
     *
759
     * @param groupRow The group record to toggle.
760
     * @example
761
     * ```typescript
762
     * public groupRow: IGroupByRecord;
763
     * const toggleExpGroup = this.grid.toggleGroup(this.groupRow);
764
     * ```
161✔
765
     */
287✔
766
    public toggleGroup(groupRow: IGroupByRecord) {
222✔
767
        this._toggleGroup(groupRow);
161✔
768
        this.notifyChanges();
161✔
769
    }
10✔
770

771
    /**
151✔
772
     * Select all rows within a group.
773
     *
774
     * @param groupRow: The group record which rows would be selected.
775
     * @param clearCurrentSelection if true clears the current selection
776
     * @example
777
     * ```typescript
78✔
778
     * this.grid.selectRowsInGroup(this.groupRow, true);
3,909!
779
     * ```
3,909✔
780
     */
781
    public selectRowsInGroup(groupRow: IGroupByRecord, clearPrevSelection?: boolean) {
782
        this._gridAPI.groupBy_select_all_rows_in_group(groupRow, clearPrevSelection);
783
        this.notifyChanges();
784
    }
785

786
    /**
787
     * Deselect all rows within a group.
788
     *
3,909✔
789
     * @param groupRow The group record which rows would be deselected.
790
     * @example
791
     * ```typescript
792
     * public groupRow: IGroupByRecord;
793
     * this.grid.deselectRowsInGroup(this.groupRow);
794
     * ```
795
     */
796
    public deselectRowsInGroup(groupRow: IGroupByRecord) {
797
        this._gridAPI.groupBy_deselect_all_rows_in_group(groupRow);
798
        this.notifyChanges();
799
    }
34,455✔
800

2,213✔
801
    /**
802
     * Expands the specified group and all of its parent groups.
803
     *
804
     * @param groupRow The group record to fully expand.
805
     * @example
806
     * ```typescript
807
     * public groupRow: IGroupByRecord;
808
     * this.grid.fullyExpandGroup(this.groupRow);
809
     * ```
810
     */
811
    public fullyExpandGroup(groupRow: IGroupByRecord) {
812
        this._fullyExpandGroup(groupRow);
813
        this.notifyChanges();
445✔
814
    }
1,375✔
815

445!
816
    /**
445!
817
     * @hidden @internal
×
818
     */
819
    public override isGroupByRecord(record: any): boolean {
445✔
820
        // return record.records instance of GroupedRecords fails under Webpack
821
        return record && record?.records && record.records?.length &&
822
         record.expression && record.expression?.fieldName;
823
    }
824

825
    /**
826
     * Toggles the expansion state of all group rows recursively.
827
     *
828
     * @example
829
     * ```typescript
830
     * this.grid.toggleAllGroupRows;
831
     * ```
832
     */
833
    public toggleAllGroupRows() {
834
        this.groupingExpansionState = [];
835
        this.groupsExpanded = !this.groupsExpanded;
15✔
836
        this.notifyChanges();
39✔
837
    }
15!
838

15✔
839
    /**
840
     * Returns if the `IgxGridComponent` has groupable columns.
841
     *
842
     * @example
89✔
843
     * ```typescript
89✔
844
     * const groupableGrid = this.grid.hasGroupableColumns;
845
     * ```
846
     */
18✔
847
    public get hasGroupableColumns(): boolean {
18✔
848
        return this._columns.some((col) => col.groupable && !col.columnGroup);
849
    }
850

851
    /**
852
     * Returns whether the `IgxGridComponent` has group area.
853
     *
854
     * @example
855
     * ```typescript
42,858✔
856
     * let isGroupAreaVisible = this.grid.showGroupArea;
42,858✔
857
     * ```
42,858✔
858
     *
123✔
859
     * @example
860
     * ```html
42,858✔
861
     * <igx-grid #grid [data]="Data" [showGroupArea]="false"></igx-grid>
20✔
862
     * ```
863
     */
864
    @Input()
42,858✔
865
    public get showGroupArea(): boolean {
41,142✔
866
        return this._showGroupArea;
867
    }
42,858✔
868
    public set showGroupArea(value: boolean) {
869
        this._showGroupArea = value;
870
        this.notifyChanges(true);
871
    }
872

873
    /**
169✔
874
     * Gets if the grid's group by drop area is visible.
169✔
875
     *
876
     * @example
877
     * ```typescript
878
     * const dropVisible = this.grid.dropAreaVisible;
879
     * ```
880
     */
3,688✔
881
    public get dropAreaVisible(): boolean {
882
        return this.columnInDrag?.groupable || !this.groupingExpressions.length;
883
    }
884

885
    /**
886
     * @hidden @internal
887
     */
61✔
888
    public override isColumnGrouped(fieldName: string): boolean {
61✔
889
        return this.groupingExpressions.find(exp => exp.fieldName === fieldName) ? true : false;
2✔
890
    }
891

61✔
892
    /**
893
     * @hidden @internal
894
     */
895
    public getContext(rowData: any, rowIndex: number, pinned?: boolean): any {
896
        if (this.isDetailRecord(rowData)) {
897
            const cachedData = this.childDetailTemplates.get(rowData.detailsData);
103✔
898
            const rowID = this.primaryKey ? rowData.detailsData[this.primaryKey] : rowData.detailsData;
899
            if (cachedData) {
31✔
900
                const view = cachedData.view;
31✔
901
                const tmlpOutlet = cachedData.owner;
31!
902
                return {
31✔
903
                    $implicit: rowData.detailsData,
904
                    moveView: view,
905
                    owner: tmlpOutlet,
103✔
906
                    index: this.dataView.indexOf(rowData),
907
                    templateID: {
908
                        type:'detailRow',
909
                        id: rowID
910
                    }
911
                };
10,937✔
912
            } else {
913
                // child rows contain unique grids, hence should have unique templates
914
                return {
915
                    $implicit: rowData.detailsData,
916
                    templateID: {
917
                        type:'detailRow',
51✔
918
                        id: rowID
919
                    },
920
                    index: this.dataView.indexOf(rowData)
921
                };
922
            }
923
        }
31✔
924
        return {
925
            $implicit: this.isGhostRecord(rowData) ? rowData.recordRef : rowData,
926
            index: this.getDataViewIndex(rowIndex, pinned),
927
            templateID: {
928
                type: this.isGroupByRecord(rowData) ? 'groupRow' : this.isSummaryRow(rowData) ? 'summaryRow' : 'dataRow',
929
                id: null
389✔
930
            },
931
            disabled: this.isGhostRecord(rowData)
932
        };
1,955✔
933
    }
117✔
934

117✔
935
    /**
936
     * @hidden @internal
937
     */
938
    public viewCreatedHandler(args) {
225✔
939
        if (args.context.templateID.type === 'detailRow') {
20✔
940
            this.childDetailTemplates.set(args.context.$implicit, args);
52✔
941
        }
1✔
942
    }
943

944
    /**
19✔
945
     * @hidden @internal
946
     */
205✔
947
    public viewMovedHandler(args) {
948
        if (args.context.templateID.type === 'detailRow') {
949
            // view was moved, update owner in cache
46✔
950
            const key = args.context.$implicit;
16✔
951
            const cachedData = this.childDetailTemplates.get(key);
6✔
952
            cachedData.owner = args.owner;
6✔
953
        }
954
    }
955

956
    /**
2✔
957
     * @hidden @internal
958
     */
959
    public get iconTemplate() {
960
        if (this.groupsExpanded) {
961
            return this.headerExpandIndicatorTemplate || this.defaultExpandedTemplate;
962
        } else {
963
            return this.headerCollapseIndicatorTemplate || this.defaultCollapsedTemplate;
964
        }
965
    }
966

967
    /**
968
     * @hidden @internal
969
     */
970
    public override ngAfterContentInit() {
971
        super.ngAfterContentInit();
972
        if (this.allowFiltering && this.hasColumnLayouts) {
973
            this.filterMode = FilterMode.excelStyleFilter;
974
        }
975
        if (this.groupTemplate) {
976
            this._groupRowTemplate = this.groupTemplate.template;
977
        }
978

979
        if (this.detailTemplateDirective) {
980
            this._detailTemplate = this.detailTemplateDirective;
981
        }
982

983

984
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
985
            this._setGroupColsVisibility(this.hideGroupedColumns);
2✔
986
        }
987
        this._setupNavigationService();
988
    }
989

990
    /**
991
     * @hidden @internal
992
     */
993
    public override ngAfterViewInit() {
994
        super.ngAfterViewInit();
995
        this.verticalScrollContainer.beforeViewDestroyed.pipe(takeUntil(this.destroy$)).subscribe((view) => {
996
            const rowData = view.context.$implicit;
997
            if (this.isDetailRecord(rowData)) {
998
                const cachedData = this.childDetailTemplates.get(rowData.detailsData);
999
                if (cachedData) {
1000
                    const tmlpOutlet = cachedData.owner;
1001
                    tmlpOutlet._viewContainerRef.detach(0);
1002
                }
1003
            }
1004
        });
1005

1006
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
1007
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
1008
                return;
1009
            }
1010

1011
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
1012
                const fieldName = sortExpr.fieldName;
1013
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
1014
                if (groupingExpr) {
1015
                    groupingExpr.dir = sortExpr.dir;
1016
                }
1017
            });
1018
        });
1019
    }
1020

1021
    /**
1022
     * @hidden @internal
1023
     */
1024
    public override ngOnInit() {
1025
        super.ngOnInit();
1026
        this.trackChanges = (_, rec) => (rec?.detailsData !== undefined ? rec.detailsData : rec);
1027
        this.groupingDone.pipe(takeUntil(this.destroy$)).subscribe((args) => {
1028
            this.crudService.endEdit(false);
1029
            this.summaryService.updateSummaryCache(args);
1030
            this._headerFeaturesWidth = NaN;
1031
        });
1032
    }
1033

1034
    /**
1035
     * @hidden @internal
1036
     */
1037
    public override ngDoCheck(): void {
1038
        if (this.groupingDiffer && this._columns && !this.hasColumnLayouts) {
1039
            const changes = this.groupingDiffer.diff(this.groupingExpressions);
1040
            if (changes && this._columns.length > 0) {
1041
                changes.forEachAddedItem((rec) => {
1042
                    const col = this.getColumnByName(rec.item.fieldName);
1043
                    if (col) {
1044
                        col.hidden = true;
1045
                    }
1046
                });
1047
                changes.forEachRemovedItem((rec) => {
1048
                    const col = this.getColumnByName(rec.item.fieldName);
1049
                    col.hidden = false;
1050
                });
1051
            }
1052
        }
1053
        super.ngDoCheck();
1054
    }
1055

1056
    /**
1057
     * @hidden @internal
1058
     */
1059
    public dataLoading(event) {
1060
        this.dataPreLoad.emit(event);
1061
    }
1062

1063
    /**
1064
     *
1065
     * Returns an array of the current cell selection in the form of `[{ column.field: cell.value }, ...]`.
1066
     *
1067
     * @remarks
1068
     * If `formatters` is enabled, the cell value will be formatted by its respective column formatter (if any).
1069
     * If `headers` is enabled, it will use the column header (if any) instead of the column field.
1070
     */
1071
    public override getSelectedData(formatters = false, headers = false): any[] {
1072
        if (this.groupingExpressions.length || this.hasDetails) {
1073
            const source = [];
1074

1075
            const process = (record) => {
1076
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
1077
                    source.push(null);
1078
                    return;
1079
                }
1080
                source.push(record);
1081

1082
            };
1083

1084
            this.dataView.forEach(process);
1085
            return this.extractDataFromSelection(source, formatters, headers);
1086
        } else {
1087
            return super.getSelectedData(formatters, headers);
1088
        }
1089
    }
1090

1091
    /**
1092
     * Returns the `IgxGridRow` by index.
1093
     *
1094
     * @example
1095
     * ```typescript
1096
     * const myRow = grid.getRowByIndex(1);
1097
     * ```
1098
     * @param index
1099
     */
1100
    public getRowByIndex(index: number): RowType {
1101
        let row: RowType;
1102
        if (index < 0) {
1103
            return undefined;
1104
        }
1105
        if (this.dataView.length >= this.virtualizationState.startIndex + this.virtualizationState.chunkSize) {
1106
            row = this.createRow(index);
1107
        } else {
1108
            if (!(index < this.virtualizationState.startIndex) && !(index > this.virtualizationState.startIndex + this.virtualizationState.chunkSize)) {
1109
                row = this.createRow(index);
1110
            }
1111
        }
1112

1113
        if (this.pagingMode === 1 && this.page !== 0) {
1114
            row.index = index + this.perPage * this.page;
1115
        }
1116
        return row;
1117
    }
1118

1119
    /**
1120
     * Returns `IgxGridRow` object by the specified primary key.
1121
     *
1122
     * @remarks
1123
     * Requires that the `primaryKey` property is set.
1124
     * @example
1125
     * ```typescript
1126
     * const myRow = this.grid1.getRowByKey("cell5");
1127
     * ```
1128
     * @param keyValue
1129
     */
1130
    public getRowByKey(key: any): RowType {
1131
        const rec = this.filteredSortedData ? this.primaryKey ?
1132
            this.filteredSortedData.find(record => record[this.primaryKey] === key) :
1133
            this.filteredSortedData.find(record => record === key) : undefined;
1134
        const index = this.dataView.indexOf(rec);
1135
        if (index < 0 || index > this.dataView.length) {
1136
            return undefined;
1137
        }
1138

1139
        return new IgxGridRow(this, index, rec);
1140
    }
1141

1142
    /**
1143
     * @hidden @internal
1144
     */
1145
    public allRows(): RowType[] {
1146
        return this.dataView.map((rec, index) => {
1147
            this.pagingMode === 1 && this.page !== 0 ? index = index + this.perPage * this.page : index = this.dataRowList.first.index + index;
1148
            return this.createRow(index);
1149
        });
1150
    }
1151

1152
    /**
1153
     * Returns the collection of `IgxGridRow`s for current page.
1154
     *
1155
     * @hidden @internal
1156
     */
1157
    public dataRows(): RowType[] {
1158
        return this.allRows().filter(row => row instanceof IgxGridRow);
1159
    }
1160

1161
    /**
1162
     * Returns an array of the selected `IgxGridCell`s.
1163
     *
1164
     * @example
1165
     * ```typescript
1166
     * const selectedCells = this.grid.selectedCells;
1167
     * ```
1168
     */
1169
    public get selectedCells(): CellType[] {
1170
        return this.dataRows().map((row) => row.cells.filter((cell) => cell.selected))
1171
            .reduce((a, b) => a.concat(b), []);
1172
    }
1173

1174
    /**
1175
     * Returns a `CellType` object that matches the conditions.
1176
     *
1177
     * @example
1178
     * ```typescript
1179
     * const myCell = this.grid1.getCellByColumn(2, "UnitPrice");
1180
     * ```
1181
     * @param rowIndex
1182
     * @param columnField
1183
     */
1184
    public getCellByColumn(rowIndex: number, columnField: string): CellType {
1185
        const row = this.getRowByIndex(rowIndex);
1186
        const column = this._columns.find((col) => col.field === columnField);
1187
        if (row && row instanceof IgxGridRow && !row.data?.detailsData && column) {
1188
            if (this.pagingMode === 1 && this.page !== 0) {
1189
                row.index = rowIndex + this.perPage * this.page;
1190
            }
1191
            return new IgxGridCell(this, row.index, column);
1192
        }
1193
    }
1194

1195
    /**
1196
     * Returns a `CellType` object that matches the conditions.
1197
     *
1198
     * @remarks
1199
     * Requires that the primaryKey property is set.
1200
     * @example
1201
     * ```typescript
1202
     * grid.getCellByKey(1, 'index');
1203
     * ```
1204
     * @param rowSelector match any rowID
1205
     * @param columnField
1206
     */
1207
    public getCellByKey(rowSelector: any, columnField: string): CellType {
1208
        const row = this.getRowByKey(rowSelector);
1209
        const column = this._columns.find((col) => col.field === columnField);
1210
        if (row && column) {
1211
            return new IgxGridCell(this, row.index, column);
1212
        }
1213
    }
1214

1215
    public override pinRow(rowID: any, index?: number): boolean {
1216
        const row = this.getRowByKey(rowID);
1217
        return super.pinRow(rowID, index, row);
1218
    }
1219

1220
    public override unpinRow(rowID: any): boolean {
1221
        const row = this.getRowByKey(rowID);
1222
        return super.unpinRow(rowID, row);
1223
    }
1224

1225
    /**
1226
     * @hidden @internal
1227
     */
1228
    public createRow(index: number, data?: any): RowType {
1229
        let row: RowType;
1230
        let rec: any;
1231

1232
        const dataIndex = this._getDataViewIndex(index);
1233
        rec = data ?? this.dataView[dataIndex];
1234

1235
        if (rec && this.isGroupByRecord(rec)) {
1236
            row = new IgxGroupByRow(this, index, rec);
1237
        }
1238
        if (rec && this.isSummaryRow(rec)) {
1239
            row = new IgxSummaryRow(this, index, rec.summaries, GridInstanceType.Grid);
1240
        }
1241
        // if found record is a no a groupby or summary row, return IgxGridRow instance
1242
        if (!row && rec) {
1243
            row = new IgxGridRow(this, index, rec);
1244
        }
1245

1246
        return row;
1247
    }
1248

1249
    /**
1250
     * @hidden @internal
1251
     */
1252
    protected override get defaultTargetBodyHeight(): number {
1253
        const allItems = this.totalItemCount || this.dataLength;
1254
        return this.renderedRowHeight * Math.min(this._defaultTargetRecordNumber,
1255
            this.paginator ? Math.min(allItems, this.perPage) : allItems);
1256
    }
1257

1258
    /**
1259
     * @hidden @internal
1260
     */
1261
    protected override getGroupAreaHeight(): number {
1262
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
1263
    }
1264

1265
    /**
1266
     * @hidden @internal
1267
     */
1268
    protected override onColumnsAddedOrRemoved() {
1269
        // update grouping states
1270
        this.groupablePipeTrigger++;
1271
        if (this.groupingExpressions && this.hideGroupedColumns) {
1272
            this._setGroupColsVisibility(this.hideGroupedColumns);
1273
        }
1274
        super.onColumnsAddedOrRemoved();
1275
    }
1276

1277
    /**
1278
     * @hidden @internal
1279
     */
1280
    protected override scrollTo(row: any | number, column: any | number): void {
1281
        if (this.groupingExpressions && this.groupingExpressions.length
1282
            && typeof (row) !== 'number') {
1283
            const rowIndex = this.groupingResult.indexOf(row);
1284
            const groupByRecord = this.groupingMetadata[rowIndex];
1285
            if (groupByRecord) {
1286
                this._fullyExpandGroup(groupByRecord);
1287
            }
1288
        }
1289

1290
        super.scrollTo(row, column, this.groupingFlatResult);
1291
    }
1292

1293
    /**
1294
     * @hidden @internal
1295
     */
1296
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
1297
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
1298
    }
1299

1300
    /**
1301
     * @hidden
1302
     */
1303
    protected _toggleGroup(groupRow: IGroupByRecord) {
1304
        this._gridAPI.groupBy_toggle_group(groupRow);
1305
    }
1306

1307
    /**
1308
     * @hidden @internal
1309
     */
1310
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
1311
        this._gridAPI.groupBy_fully_expand_group(groupRow);
1312
    }
1313

1314
    /**
1315
     * @hidden @internal
1316
     */
1317
    protected _applyGrouping() {
1318
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
1319
    }
1320

1321
    private _setupNavigationService() {
1322
        if (this.hasColumnLayouts) {
1323
            this.navigation = new IgxGridMRLNavigationService(this.platform);
1324
            this.navigation.grid = this;
1325
        }
1326
    }
1327

1328
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
1329
        if (expression instanceof Array) {
1330
            for (const singleExpression of expression) {
1331
                if (!singleExpression.fieldName) {
1332
                    return true;
1333
                }
1334
            }
1335
            return false;
1336
        }
1337
        return !expression.fieldName;
1338
    }
1339

1340
    private _setGroupColsVisibility(value) {
1341
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
1342
            this.groupingExpressions.forEach((expr) => {
1343
                const col = this.getColumnByName(expr.fieldName);
1344
                col.hidden = value;
1345
            });
1346
        }
1347
    }
1348
}
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