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

IgniteUI / igniteui-angular / 13331632524

14 Feb 2025 02:51PM CUT coverage: 22.015% (-69.6%) from 91.622%
13331632524

Pull #15372

github

web-flow
Merge d52d57714 into bcb78ae0a
Pull Request #15372: chore(*): test ci passing

1990 of 15592 branches covered (12.76%)

431 of 964 new or added lines in 18 files covered. (44.71%)

19956 existing lines in 307 files now uncovered.

6452 of 29307 relevant lines covered (22.02%)

249.17 hits per line

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

29.02
/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 { NgIf, NgTemplateOutlet, NgClass, NgFor, 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
        NgIf,
120
        NgClass,
121
        NgFor,
122
        NgStyle,
123
        NgTemplateOutlet,
124
        IgxGridGroupByAreaComponent,
125
        IgxGridHeaderRowComponent,
126
        IgxGridBodyDirective,
127
        IgxGridDragSelectDirective,
128
        IgxColumnMovingDropDirective,
129
        IgxGridForOfDirective,
130
        IgxTemplateOutletDirective,
131
        IgxGridRowComponent,
132
        IgxGridGroupByRowComponent,
133
        IgxSummaryRowComponent,
134
        IgxOverlayOutletDirective,
135
        IgxToggleDirective,
136
        IgxCircularProgressBarComponent,
137
        IgxSnackbarComponent,
138
        IgxButtonDirective,
139
        IgxRippleDirective,
140
        IgxIconComponent,
141
        IgxRowEditTabStopDirective,
142
        IgxGridColumnResizerComponent,
143
        IgxGridTransactionPipe,
144
        IgxHasVisibleColumnsPipe,
145
        IgxGridRowPinningPipe,
146
        IgxGridAddRowPipe,
147
        IgxGridRowClassesPipe,
148
        IgxGridRowStylesPipe,
149
        IgxSummaryDataPipe,
150
        IgxGridGroupingPipe,
151
        IgxGridPagingPipe,
152
        IgxGridSortingPipe,
153
        IgxGridFilteringPipe,
154
        IgxGridSummaryPipe,
155
        IgxGridDetailsPipe,
156
        IgxStringReplacePipe
157
    ],
158
    schemas: [CUSTOM_ELEMENTS_SCHEMA]
159
})
160
export class IgxGridComponent extends IgxGridBaseDirective implements GridType, OnInit, DoCheck, AfterContentInit, AfterViewInit {
2✔
161
    /**
162
     * Emitted when a new chunk of data is loaded from virtualization.
163
     *
164
     * @example
165
     * ```typescript
166
     *  <igx-grid #grid [data]="localData" [autoGenerate]="true" (dataPreLoad)='handleDataPreloadEvent()'></igx-grid>
167
     * ```
168
     */
169
    @Output()
170
    public dataPreLoad = new EventEmitter<IForOfState>();
36✔
171

172
    /**
173
     * @hidden
174
     */
175
    @Output()
176
    public groupingExpressionsChange = new EventEmitter<IGroupingExpression[]>();
36✔
177

178
    /**
179
     * @hidden @internal
180
     */
181
    @Output()
182
    public groupingExpansionStateChange = new EventEmitter<IGroupByExpandState[]>();
36✔
183

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

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

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

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

239

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

274
    /**
275
     * @hidden @internal
276
     */
277
    @HostBinding('attr.role')
278
    public role = 'grid';
36✔
279

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

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

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

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

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

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

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

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

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

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

344
    /**
345
     * @hidden @internal
346
     */
347
    public groupingMetadata: any[];
348

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

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

380
    private _groupByRowSelectorTemplate: TemplateRef<IgxGroupByRowSelectorTemplateContext>;
381
    private _detailTemplate;
382

383

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

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

407
        if (this.autoGenerate && this._data.length > 0 && this.shouldRecreateColumns(oldData, this._data)) {
37!
UNCOV
408
            this.setupColumns();
×
409
        }
410

411
        this.cdr.markForCheck();
37✔
412
        if (this.isPercentHeight) {
37!
UNCOV
413
            this.notifyChanges(true);
×
414
        }
415
        // check if any columns have width auto and if so recalculate their auto-size on data loaded.
416
        if (dataLoaded && this._columns.some(x => (x as any)._width === 'auto')) {
37!
UNCOV
417
            this.recalculateAutoSizes();
×
418
        }
419
        this.checkPrimaryKeyField();
37✔
420
    }
421

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

438
    public get totalItemCount() {
UNCOV
439
        return this.verticalScrollContainer.totalItemCount;
×
440
    }
441

442
    private get _gridAPI(): IgxGridAPIService {
443
        return this.gridAPI as IgxGridAPIService;
12✔
444
    }
445

446
    private childDetailTemplates: Map<any, any> = new Map();
36✔
447

448
    /**
449
     * @hidden @internal
450
     */
451
    public groupingPerformedSubject = new Subject<void>();
36✔
452

453
    /**
454
     * @hidden @internal
455
     */
456
    public groupingPerformed$: Observable<void> = this.groupingPerformedSubject.asObservable();
36✔
457

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

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

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

535
    public set groupingExpansionState(value) {
UNCOV
536
        if (value !== this._groupingExpandState) {
×
UNCOV
537
            this.groupingExpansionStateChange.emit(value);
×
538
        }
UNCOV
539
        this._groupingExpandState = value;
×
UNCOV
540
        if (this.gridAPI.grid) {
×
UNCOV
541
            this.cdr.detectChanges();
×
542
        }
543
    }
544

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

560
    public set hideGroupedColumns(value: boolean) {
UNCOV
561
        if (value) {
×
UNCOV
562
            this.groupingDiffer = this.differs.find(this.groupingExpressions).create();
×
563
        } else {
UNCOV
564
            this.groupingDiffer = null;
×
565
        }
UNCOV
566
        if (this._columns && this.groupingExpressions) {
×
UNCOV
567
            this._setGroupColsVisibility(value);
×
568
        }
569

UNCOV
570
        this._hideGroupedColumns = value;
×
571
    }
572

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

588
    public set groupStrategy(value: IGridGroupingStrategy) {
UNCOV
589
        this._groupStrategy = value;
×
590
    }
591

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

610
    public get dropAreaMessage(): string {
UNCOV
611
        return this._dropAreaMessage || this.resourceStrings.igx_grid_groupByArea_message;
×
612
    }
613

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

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

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

653
    /**
654
     * @hidden @internal
655
     */
656
    public getDetailsContext(rowData, index): IgxGridDetailTemplateDirective {
UNCOV
657
        return {
×
658
            $implicit: rowData,
659
            index
660
        };
661
    }
662

663
    /**
664
     * @hidden @internal
665
     */
666
    public detailsViewFocused(container, rowIndex) {
UNCOV
667
        this.navigation.setActiveNode({ row: rowIndex });
×
668
    }
669

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

677
    /**
678
     * @hidden @internal
679
     */
680
    public getRowTemplate(rowData) {
681
        if (this.isGroupByRecord(rowData)) {
1,392!
UNCOV
682
            return this.defaultGroupTemplate;
×
683
        } else if (this.isSummaryRow(rowData)) {
1,392!
UNCOV
684
            return this.summaryTemplate;
×
685
        } else if (this.hasDetails && this.isDetailRecord(rowData)) {
1,392!
UNCOV
686
            return this.detailTemplateContainer;
×
687
        } else {
688
            return this.recordTemplate;
1,392✔
689
        }
690
    }
691

692
    /**
693
     * @hidden @internal
694
     */
695
    public override isDetailRecord(record) {
696
        return record && record.detailsData !== undefined;
1,480✔
697
    }
698

699
    /**
700
     * @hidden @internal
701
     */
702
    public isDetailActive(rowIndex) {
UNCOV
703
        return this.navigation.activeNode ? this.navigation.activeNode.row === rowIndex : false;
×
704
    }
705

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

720
    public set groupRowTemplate(template: TemplateRef<IgxGroupByRowTemplateContext>) {
UNCOV
721
        this._groupRowTemplate = template;
×
UNCOV
722
        this.notifyChanges();
×
723
    }
724

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

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

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

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

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

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

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

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

853
    /**
854
     * @hidden @internal
855
     */
856
    public override isGroupByRecord(record: any): boolean {
857
        // return record.records instance of GroupedRecords fails under Webpack
858
        return record && record?.records && record.records?.length &&
3,260!
859
            record.expression && record.expression?.fieldName;
860
    }
861

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

876
    /** @hidden @internal */
877
    public get hasGroupableColumns(): boolean {
878
        return this._columns.some((col) => col.groupable && !col.columnGroup);
2,682!
879
    }
880

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

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

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

953
    /**
954
     * @hidden @internal
955
     */
956
    public viewCreatedHandler(args) {
957
        if (args.context.templateID.type === 'detailRow') {
260!
UNCOV
958
            this.childDetailTemplates.set(args.context.$implicit, args);
×
959
        }
960
    }
961

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

974
    /**
975
     * @hidden @internal
976
     */
977
    public get iconTemplate() {
UNCOV
978
        if (this.groupsExpanded) {
×
UNCOV
979
            return this.headerExpandedIndicatorTemplate || this.defaultExpandedTemplate;
×
980
        } else {
UNCOV
981
            return this.headerCollapsedIndicatorTemplate || this.defaultCollapsedTemplate;
×
982
        }
983
    }
984

985
    /**
986
     * @hidden @internal
987
     */
988
    public override ngAfterContentInit() {
989
        super.ngAfterContentInit();
36✔
990
        if (this.allowFiltering && this.hasColumnLayouts) {
36!
UNCOV
991
            this.filterMode = FilterMode.excelStyleFilter;
×
992
        }
993
        if (this.groupTemplate) {
36!
UNCOV
994
            this._groupRowTemplate = this.groupTemplate.template;
×
995
        }
996

997
        if (this.detailTemplateDirective) {
36!
UNCOV
998
            this._detailTemplate = this.detailTemplateDirective;
×
999
        }
1000

1001

1002
        if (this.hideGroupedColumns && this._columns && this.groupingExpressions) {
36!
UNCOV
1003
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1004
        }
1005
        this._setupNavigationService();
36✔
1006
    }
1007

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

1024
        this.sortingExpressionsChange.pipe(takeUntil(this.destroy$)).subscribe((sortingExpressions: ISortingExpression[]) => {
36✔
UNCOV
1025
            if (!this.groupingExpressions || !this.groupingExpressions.length) {
×
UNCOV
1026
                return;
×
1027
            }
1028

UNCOV
1029
            sortingExpressions.forEach((sortExpr: ISortingExpression) => {
×
UNCOV
1030
                const fieldName = sortExpr.fieldName;
×
UNCOV
1031
                const groupingExpr = this.groupingExpressions.find(ex => ex.fieldName === fieldName);
×
UNCOV
1032
                if (groupingExpr) {
×
UNCOV
1033
                    groupingExpr.dir = sortExpr.dir;
×
1034
                }
1035
            });
1036
        });
1037
    }
1038

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

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

1074
    /**
1075
     * @hidden @internal
1076
     */
1077
    public dataLoading(event) {
UNCOV
1078
        this.dataPreLoad.emit(event);
×
1079
    }
1080

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

UNCOV
1093
            const process = (record) => {
×
UNCOV
1094
                if (record.expression || record.summaries || this.isDetailRecord(record)) {
×
UNCOV
1095
                    source.push(null);
×
UNCOV
1096
                    return;
×
1097
                }
UNCOV
1098
                source.push(record);
×
1099

1100
            };
1101

UNCOV
1102
            this.dataView.forEach(process);
×
UNCOV
1103
            return this.extractDataFromSelection(source, formatters, headers);
×
1104
        } else {
UNCOV
1105
            return super.getSelectedData(formatters, headers);
×
1106
        }
1107
    }
1108

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

UNCOV
1131
        if (this.pagingMode === 1 && this.page !== 0) {
×
UNCOV
1132
            row.index = index + this.perPage * this.page;
×
1133
        }
UNCOV
1134
        return row;
×
1135
    }
1136

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

UNCOV
1157
        return new IgxGridRow(this, index, rec);
×
1158
    }
1159

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

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

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

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

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

1233
    public override pinRow(rowID: any, index?: number): boolean {
UNCOV
1234
        const row = this.getRowByKey(rowID);
×
UNCOV
1235
        return super.pinRow(rowID, index, row);
×
1236
    }
1237

1238
    public override unpinRow(rowID: any): boolean {
UNCOV
1239
        const row = this.getRowByKey(rowID);
×
UNCOV
1240
        return super.unpinRow(rowID, row);
×
1241
    }
1242

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

UNCOV
1249
        const dataIndex = this._getDataViewIndex(index);
×
UNCOV
1250
        const rec = data ?? this.dataView[dataIndex];
×
1251

UNCOV
1252
        if (rec && this.isGroupByRecord(rec)) {
×
UNCOV
1253
            row = new IgxGroupByRow(this, index, rec);
×
1254
        }
UNCOV
1255
        if (rec && this.isSummaryRow(rec)) {
×
UNCOV
1256
            row = new IgxSummaryRow(this, index, rec.summaries);
×
1257
        }
1258
        // if found record is a no a groupby or summary row, return IgxGridRow instance
UNCOV
1259
        if (!row && rec) {
×
UNCOV
1260
            row = new IgxGridRow(this, index, rec);
×
1261
        }
1262

UNCOV
1263
        return row;
×
1264
    }
1265

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

1275
    /**
1276
     * @hidden @internal
1277
     */
1278
    protected override getGroupAreaHeight(): number {
1279
        return this.groupArea ? this.getComputedHeight(this.groupArea.nativeElement) : 0;
102!
1280
    }
1281

1282
    /**
1283
     * @hidden @internal
1284
     */
1285
    protected override onColumnsAddedOrRemoved() {
1286
        // update grouping states
1287
        this.groupablePipeTrigger++;
1✔
1288
        if (this.groupingExpressions && this.hideGroupedColumns) {
1!
UNCOV
1289
            this._setGroupColsVisibility(this.hideGroupedColumns);
×
1290
        }
1291
        super.onColumnsAddedOrRemoved();
1✔
1292
    }
1293

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

UNCOV
1307
        super.scrollTo(row, column, this.groupingFlatResult);
×
1308
    }
1309

1310
    /**
1311
     * @hidden @internal
1312
     */
1313
    protected _getStateForGroupRow(groupRow: IGroupByRecord): IGroupByExpandState {
UNCOV
1314
        return this._gridAPI.groupBy_get_expanded_for_group(groupRow);
×
1315
    }
1316

1317
    /**
1318
     * @hidden
1319
     */
1320
    protected _toggleGroup(groupRow: IGroupByRecord) {
UNCOV
1321
        this._gridAPI.groupBy_toggle_group(groupRow);
×
1322
    }
1323

1324
    /**
1325
     * @hidden @internal
1326
     */
1327
    protected _fullyExpandGroup(groupRow: IGroupByRecord) {
UNCOV
1328
        this._gridAPI.groupBy_fully_expand_group(groupRow);
×
1329
    }
1330

1331
    /**
1332
     * @hidden @internal
1333
     */
1334
    protected _applyGrouping() {
1335
        this._gridAPI.sort_groupBy_multiple(this._groupingExpressions);
6✔
1336
    }
1337

1338
    private _setupNavigationService() {
1339
        if (this.hasColumnLayouts) {
36!
UNCOV
1340
            this.navigation = new IgxGridMRLNavigationService(this.platform);
×
UNCOV
1341
            this.navigation.grid = this;
×
1342
        }
1343
    }
1344

1345
    private checkIfNoColumnField(expression: IGroupingExpression | Array<IGroupingExpression> | any): boolean {
UNCOV
1346
        if (expression instanceof Array) {
×
UNCOV
1347
            for (const singleExpression of expression) {
×
UNCOV
1348
                if (!singleExpression.fieldName) {
×
UNCOV
1349
                    return true;
×
1350
                }
1351
            }
UNCOV
1352
            return false;
×
1353
        }
UNCOV
1354
        return !expression.fieldName;
×
1355
    }
1356

1357
    private _setGroupColsVisibility(value) {
UNCOV
1358
        if (this._columns.length > 0 && !this.hasColumnLayouts) {
×
UNCOV
1359
            this.groupingExpressions.forEach((expr) => {
×
UNCOV
1360
                const col = this.getColumnByName(expr.fieldName);
×
UNCOV
1361
                col.hidden = value;
×
1362
            });
1363
        }
1364
    }
1365

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

© 2025 Coveralls, Inc