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

IgniteUI / igniteui-angular / 17322895485

29 Aug 2025 11:43AM UTC coverage: 91.568% (-0.05%) from 91.618%
17322895485

push

github

web-flow
Grid Cell merging  (#16024)

13819 of 16209 branches covered (85.26%)

234 of 254 new or added lines in 11 files covered. (92.13%)

16 existing lines in 2 files now uncovered.

27757 of 30313 relevant lines covered (91.57%)

34712.72 hits per line

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

92.53
/projects/igniteui-angular/src/lib/grids/row.directive.ts
1
import {
2
    AfterViewInit,
3
    booleanAttribute,
4
    ChangeDetectorRef,
5
    Directive,
6
    DoCheck,
7
    ElementRef,
8
    EventEmitter,
9
    forwardRef,
10
    HostBinding,
11
    HostListener,
12
    Inject,
13
    Input,
14
    OnDestroy,
15
    Output,
16
    QueryList,
17
    ViewChild,
18
    ViewChildren
19
} from '@angular/core';
20
import { IgxCheckboxComponent } from '../checkbox/checkbox.component';
21
import { IgxGridForOfDirective } from '../directives/for-of/for_of.directive';
22
import { TransactionType } from '../services/transaction/transaction';
23
import { IgxGridSelectionService } from './selection/selection.service';
24
import { IgxEditRow } from './common/crud.service';
25
import { CellType, ColumnType, GridType, IGX_GRID_BASE } from './common/grid.interface';
26
import { mergeWith } from 'lodash-es';
27
import { Subject } from 'rxjs';
28
import { takeUntil } from 'rxjs/operators';
29
import { trackByIdentity } from '../core/utils';
30

31
@Directive({
32
    selector: '[igxRowBaseComponent]',
33
    standalone: true
34
})
35
export class IgxRowDirective implements DoCheck, AfterViewInit, OnDestroy {
3✔
36
    /**
37
     * @hidden
38
     */
39
    @Output()
40
    public addAnimationEnd = new EventEmitter<IgxRowDirective>();
30,690✔
41

42
    /**
43
     * @hidden
44
     */
45
    @HostBinding('attr.role')
46
    public role = 'row';
30,690✔
47

48
    /**
49
     * @hidden
50
     */
51
    @Input()
52
    public metaData: any;
53

54
    /**
55
     *  The data passed to the row component.
56
     *
57
     * ```typescript
58
     * // get the row data for the first selected row
59
     * let selectedRowData = this.grid.selectedRows[0].data;
60
     * ```
61
     */
62
    @Input()
63
    public get data(): any {
64
        if (this.inEditMode) {
9,527,845✔
65
            return mergeWith(this.grid.dataCloneStrategy.clone(this._data), this.grid.transactions.getAggregatedValue(this.key, false),
46,812✔
66
                (objValue, srcValue) => {
67
                    if (Array.isArray(srcValue)) {
10,196!
68
                        return objValue = srcValue;
×
69
                    }
70
                });
71
        }
72
        return this._data;
9,481,033✔
73
    }
74

75
    public set data(v: any) {
76
        this._data = v;
40,980✔
77
    }
78
    /**
79
     * The index of the row.
80
     *
81
     * ```typescript
82
     * // get the index of the second selected row
83
     * let selectedRowIndex = this.grid.selectedRows[1].index;
84
     * ```
85
     */
86
    @Input()
87
    public index: number;
88

89
    /**
90
     * Sets whether this specific row has disabled functionality for editing and row selection.
91
     * Default value is `false`.
92
     * ```typescript
93
     * this.grid.selectedRows[0].pinned = true;
94
     * ```
95
     */
96
    @Input({ transform: booleanAttribute })
97
    @HostBinding('attr.aria-disabled')
98
    @HostBinding('class.igx-grid__tr--disabled')
99
    public disabled = false;
30,690✔
100

101
    /**
102
     * Sets whether the row is pinned.
103
     * Default value is `false`.
104
     * ```typescript
105
     * this.grid.selectedRows[0].pinned = true;
106
     * ```
107
     */
108
    public set pinned(value: boolean) {
109
        if (value) {
11✔
110
            this.grid.pinRow(this.key);
6✔
111
        } else {
112
            this.grid.unpinRow(this.key);
5✔
113
        }
114
    }
115

116
    /**
117
     * Gets whether the row is pinned.
118
     * ```typescript
119
     * let isPinned = row.pinned;
120
     * ```
121
     */
122
    public get pinned(): boolean {
123
        return this.grid.isRecordPinned(this.data);
1,116,097✔
124
    }
125

126
    public get hasMergedCells(): boolean {
127
            return this.grid.columnsToMerge.length > 0;
1,125,594✔
128
    }
129

130
    /**
131
     * Gets the expanded state of the row.
132
     * ```typescript
133
     * let isExpanded = row.expanded;
134
     * ```
135
     */
136
    public get expanded(): boolean {
137
        return this.grid.gridAPI.get_row_expansion_state(this.data);
6,392✔
138
    }
139

140
    /**
141
     * Expands/collapses the current row.
142
     *
143
     * ```typescript
144
     * this.grid.selectedRows[2].expanded = true;
145
     * ```
146
     */
147
    public set expanded(val: boolean) {
148
        this.grid.gridAPI.set_row_expansion_state(this.key, val);
×
149
    }
150

151
    public get addRowUI(): any {
152
        return !!this.grid.crudService.row &&
1,697,105✔
153
            this.grid.crudService.row.isAddRow &&
154
            this.grid.crudService.row.id === this.key;
155
    }
156

157
    @HostBinding('style.min-height.px')
158
    public get rowHeight() {
159
        let height = this.grid.rowHeight || 32;
246,720!
160
        if (this.grid.hasColumnLayouts) {
246,720✔
161
            const maxRowSpan = this.grid.multiRowLayoutRowSize;
5,049✔
162
            height = height * maxRowSpan;
5,049✔
163
        }
164
        return this.addRowUI ? height : null;
246,720✔
165
    }
166

167
    /**
168
     * @hidden
169
     */
170
    @Input()
171
    public gridID: string;
172

173
    /**
174
     * @hidden
175
     */
176
    @ViewChildren('igxDirRef', { read: IgxGridForOfDirective })
177
    public _virtDirRow: QueryList<IgxGridForOfDirective<ColumnType, ColumnType[]>>;
178

179
    /* blazorSuppress */
180
    public get virtDirRow(): IgxGridForOfDirective<ColumnType, ColumnType[]> {
181
        return this._virtDirRow ? this._virtDirRow.first : null;
95,441!
182
    }
183

184
    /**
185
     * @hidden
186
     */
187
    @ViewChild(forwardRef(() => IgxCheckboxComponent), { read: IgxCheckboxComponent })
3✔
188
    public checkboxElement: IgxCheckboxComponent;
189

190
    @ViewChildren('cell')
191
    protected _cells: QueryList<CellType>;
192

193
    /**
194
     * Gets the rendered cells in the row component.
195
     *
196
     * ```typescript
197
     * // get the cells of the third selected row
198
     * let selectedRowCells = this.grid.selectedRows[2].cells;
199
     * ```
200
     */
201
    public get cells() {
202
        const res = new QueryList<CellType>();
21,407✔
203
        if (!this._cells) {
21,407!
204
            return res;
×
205
        }
206
        const cList = this._cells.filter((item) => item.nativeElement.parentElement !== null)
108,402✔
207
            .sort((item1, item2) => item1.column.visibleIndex - item2.column.visibleIndex);
116,249✔
208
        res.reset(cList);
21,407✔
209
        return res;
21,407✔
210
    }
211

212
    @HostBinding('attr.data-rowIndex')
213
    public get dataRowIndex() {
214
        return this.index;
246,721✔
215
    }
216

217
    /**
218
     * @hidden
219
     */
220
    @Input()
221
    @HostBinding('attr.aria-selected')
222
    public get selected(): boolean {
223
        return this.selectionService.isRowSelected(this.key);
1,680,428✔
224
    }
225

226
    public set selected(value: boolean) {
227
        if (value) {
3✔
228
            this.selectionService.selectRowsWithNoEvent([this.key]);
2✔
229
        } else {
230
            this.selectionService.deselectRowsWithNoEvent([this.key]);
1✔
231
        }
232
        this.grid.cdr.markForCheck();
3✔
233
    }
234

235
    /**
236
     * @hidden
237
     */
238
    public get columns(): ColumnType[] {
239
        return this.grid.visibleColumns;
×
240
    }
241

242
    /**
243
     * @hidden
244
     * @internal
245
     */
246
    public get viewIndex(): number {
247
        if ((this.grid as any).groupingExpressions.length) {
1,562,281✔
248
            return this.grid.filteredSortedData.indexOf(this.data);
126,566✔
249
        }
250
        return this.index + this.grid.page * this.grid.perPage;
1,435,715✔
251
    }
252

253
    /**
254
     * @hidden
255
     */
256
    public get pinnedColumns(): ColumnType[] {
257
        return this.grid.pinnedColumns;
×
258
    }
259

260
    /**
261
     * @hidden
262
     */
263
    public get pinnedStartColumns(): ColumnType[] {
264
        return this.grid.pinnedStartColumns;
255,093✔
265
    }
266

267
    /**
268
    * @hidden
269
    */
270
    public get pinnedEndColumns(): ColumnType[] {
271
        return this.grid.pinnedEndColumns;
242,021✔
272
    }
273

274
    /**
275
     * @hidden
276
     */
277
    public get isRoot(): boolean {
278
        return true;
10✔
279
    }
280

281
    /**
282
     * @hidden
283
     */
284
    public get hasChildren(): boolean {
285
        return false;
×
286
    }
287

288
    /**
289
     * @hidden
290
     */
291
    public get unpinnedColumns(): ColumnType[] {
292
        return this.grid.unpinnedColumns;
253,853✔
293
    }
294

295
    /**
296
     * @hidden
297
     */
298
    public get showRowSelectors(): boolean {
299
        return this.grid.showRowSelectors;
241,340✔
300
    }
301

302
    /** @hidden */
303
    public get dirty(): boolean {
304
        const row = this.grid.transactions.getState(this.key);
246,720✔
305
        if (row) {
246,720✔
306
            return row.type === TransactionType.ADD || row.type === TransactionType.UPDATE;
585✔
307
        }
308

309
        return false;
246,135✔
310
    }
311

312
    /**
313
     * @hidden
314
     */
315
    public get rowDraggable(): boolean {
316
        return this.grid.rowDraggable;
241,340✔
317
    }
318

319
    /** @hidden */
320
    public get added(): boolean {
321
        const row = this.grid.transactions.getState(this.key);
46,223✔
322
        if (row) {
46,223✔
323
            return row.type === TransactionType.ADD;
15✔
324
        }
325

326
        return false;
46,208✔
327
    }
328

329
    /** @hidden */
330
    public get deleted(): boolean {
331
        return this.grid.gridAPI.row_deleted_transaction(this.key);
325,954✔
332
    }
333

334
    /**
335
     * @hidden
336
     */
337
    public get dragging() {
338
        return this.grid.dragRowID === this.key;
246,943✔
339
    }
340

341
    // TODO: Refactor
342
    public get inEditMode(): boolean {
343
        if (this.grid.rowEditable) {
9,763,689✔
344
            const editRowState = this.grid.crudService.row;
1,435,520✔
345
            return (editRowState && editRowState.id === this.key) || false;
1,435,520✔
346
        } else {
347
            return false;
8,328,169✔
348
        }
349
    }
350

351
    /**
352
     * Gets the ID of the row.
353
     * A row in the grid is identified either by:
354
     * - primaryKey data value,
355
     * - the whole data, if the primaryKey is omitted.
356
     *
357
     * ```typescript
358
     * let rowID = this.grid.selectedRows[2].key;
359
     * ```
360
     */
361
    public get key() {
362
        const primaryKey = this.grid.primaryKey;
10,357,435✔
363
        if (this._data) {
10,357,435!
364
            return primaryKey ? this._data[primaryKey] : this._data;
10,357,435✔
365
        } else {
366
            return undefined;
×
367
        }
368
    }
369

370
    /**
371
     * The native DOM element representing the row. Could be null in certain environments.
372
     *
373
     * ```typescript
374
     * // get the nativeElement of the second selected row
375
     * let selectedRowNativeElement = this.grid.selectedRows[1].nativeElement;
376
     * ```
377
     */
378
    public get nativeElement() {
379
        return this.element.nativeElement;
68,091✔
380
    }
381

382
    /**
383
     * @hidden
384
     */
385
    public focused = false;
30,690✔
386

387
    /**
388
     * @hidden
389
     * @internal
390
     */
391
    public defaultCssClass = 'igx-grid__tr';
30,690✔
392

393
    /**
394
     * @hidden
395
     */
396
    public triggerAddAnimationClass = false;
30,690✔
397

398
    protected destroy$ = new Subject<any>();
30,690✔
399
    protected _data: any;
400
    protected _addRow: boolean;
401

402
    constructor(
403
        @Inject(IGX_GRID_BASE) public grid: GridType,
30,690✔
404
        public selectionService: IgxGridSelectionService,
30,690✔
405
        public element: ElementRef<HTMLElement>,
30,690✔
406
        public cdr: ChangeDetectorRef) { }
30,690✔
407

408
    /**
409
     * @hidden
410
     * @internal
411
     */
412
    @HostListener('click', ['$event'])
413
    public onClick(event: MouseEvent) {
414
        if (this.hasMergedCells && this.metaData?.cellMergeMeta) {
337✔
415
            const targetRowIndex = this.grid.navigation.activeNode.row;
2✔
416
            if (targetRowIndex != this.index) {
2!
NEW
417
                const row = this.grid.rowList.toArray().find(x => x.index === targetRowIndex);
×
NEW
418
                row.onClick(event);
×
NEW
419
                return;
×
420
            }
421
        }
422
        this.grid.rowClick.emit({
337✔
423
            row: this,
424
            event
425
        });
426

427
        if (this.grid.rowSelection === 'none' || this.deleted || !this.grid.selectRowOnClick) {
337✔
428
            return;
274✔
429
        }
430
        if (event.shiftKey && this.grid.isMultiRowSelectionEnabled) {
63✔
431
            this.selectionService.selectMultipleRows(this.key, this.data, event);
6✔
432
            return;
6✔
433
        }
434

435
        const clearSelection = !(+event.ctrlKey ^ +event.metaKey);
57✔
436
        if (this.selected && !clearSelection) {
57✔
437
            this.selectionService.deselectRow(this.key, event);
6✔
438
        } else {
439
            this.selectionService.selectRowById(this.key, clearSelection, event);
51✔
440
        }
441
    }
442

443
    /**
444
     * @hidden
445
     * @internal
446
     */
447
    @HostListener('contextmenu', ['$event'])
448
    public onContextMenu(event: MouseEvent) {
449
        const cell = (event.target as HTMLElement).closest('.igx-grid__td');
3✔
450
        this.grid.contextMenu.emit({
3✔
451
            row: this,
452
            cell: this.cells.find(c => c.nativeElement === cell),
7✔
453
            event
454
        });
455
    }
456

457
    /**
458
     * @hidden
459
     * @internal
460
     */
461
    @HostListener('mouseenter')
462
    public showActionStrip() {
463
        if (this.grid.actionStrip) {
4✔
464
            this.grid.actionStrip.show(this);
3✔
465
        }
466
        this.grid.hoverIndex = this.index;
4✔
467
    }
468

469
    /**
470
     * @hidden
471
     * @internal
472
     */
473
    @HostListener('mouseleave')
474
    public hideActionStrip() {
475
        if (this.grid.actionStrip && this.grid.actionStrip.hideOnRowLeave) {
1✔
476
            this.grid.actionStrip.hide();
1✔
477
        }
478
        this.grid.hoverIndex = null;
1✔
479
    }
480

481
    /**
482
     * @hidden
483
     * @internal
484
     */
485
    public ngAfterViewInit() {
486
        // If the template of the row changes, the forOf in it is recreated and is not detected by the grid and rows can't be scrolled.
487
        this._virtDirRow.changes.pipe(takeUntil(this.destroy$)).subscribe(() => this.grid.resetHorizontalVirtualization());
30,690✔
488
    }
489

490
    /**
491
     * @hidden
492
     * @internal
493
     */
494
    public ngOnDestroy() {
495
        this.destroy$.next(true);
30,400✔
496
        this.destroy$.complete();
30,400✔
497
    }
498

499
    /**
500
     * @hidden
501
     */
502
    public onRowSelectorClick(event) {
503
        event.stopPropagation();
113✔
504
        if (event.shiftKey && this.grid.isMultiRowSelectionEnabled) {
113!
505
            this.selectionService.selectMultipleRows(this.key, this.data, event);
×
506
            return;
×
507
        }
508
        if (this.selected) {
113✔
509
            this.selectionService.deselectRow(this.key, event);
23✔
510
        } else {
511
            this.selectionService.selectRowById(this.key, false, event);
90✔
512
        }
513
    }
514

515
    /**
516
     * Updates the specified row object and the data source record with the passed value.
517
     *
518
     * ```typescript
519
     * // update the second selected row's value
520
     * let newValue = "Apple";
521
     * this.grid.selectedRows[1].update(newValue);
522
     * ```
523
     */
524
    public update(value: any) {
525
        const crudService = this.grid.crudService;
1✔
526
        if (crudService.cellInEditMode && crudService.cell.id.key === this.key) {
1!
527
            this.grid.transactions.endPending(false);
×
528
        }
529
        const row = new IgxEditRow(this.key, this.index, this.data, this.grid);
1✔
530
        this.grid.gridAPI.update_row(row, value);
1✔
531
        this.cdr.markForCheck();
1✔
532
    }
533

534
    /**
535
     * Removes the specified row from the grid's data source.
536
     * This method emits `rowDeleted` event.
537
     *
538
     * ```typescript
539
     * // delete the third selected row from the grid
540
     * this.grid.selectedRows[2].delete();
541
     * ```
542
     */
543
    public delete() {
544
        this.grid.deleteRowById(this.key);
5✔
545
    }
546

547
    public isCellActive(visibleColumnIndex) {
548
        const node = this.grid.navigation.activeNode;
1,168,352✔
549
        const field = this.grid.visibleColumns[visibleColumnIndex]?.field;
1,168,352✔
550
        const rowSpan = this.metaData?.cellMergeMeta?.get(field)?.rowSpan;
1,168,352✔
551
        if (rowSpan > 1) {
1,168,352✔
552
            return node ? (node.row >= this.index && node.row < this.index + rowSpan)
535!
553
             && node.column === visibleColumnIndex : false;
554
        }
555
        return node ? node.row === this.index && node.column === visibleColumnIndex : false;
1,167,817✔
556
    }
557

558
    /**
559
     * Pins the specified row.
560
     * This method emits `rowPinning`\`rowPinned` event.
561
     *
562
     * ```typescript
563
     * // pin the selected row from the grid
564
     * this.grid.selectedRows[0].pin();
565
     * ```
566
     */
567
    public pin() {
568
        return this.grid.pinRow(this.key);
39✔
569
    }
570

571
    /**
572
     * Unpins the specified row.
573
     * This method emits `rowPinning`\`rowPinned` event.
574
     *
575
     * ```typescript
576
     * // unpin the selected row from the grid
577
     * this.grid.selectedRows[0].unpin();
578
     * ```
579
     */
580
    public unpin() {
581
        return this.grid.unpinRow(this.key);
4✔
582
    }
583

584
    /**
585
     * @hidden
586
     */
587
    public get rowCheckboxAriaLabel() {
588
        return this.grid.primaryKey ?
17,551✔
589
            this.selected ? 'Deselect row with key ' + this.key : 'Select row with key ' + this.key :
15,738✔
590
            this.selected ? 'Deselect row' : 'Select row';
1,813✔
591
    }
592

593
    /**
594
     * @hidden
595
     */
596
    public ngDoCheck() {
597
        this.cdr.markForCheck();
246,720✔
598
    }
599

600
    /**
601
     * @hidden
602
     */
603
    public shouldDisplayPinnedChip(col: ColumnType): boolean {
604
        return this.pinned && this.disabled && col.visibleIndex === 0 && !this.metaData?.cellMergeMeta?.get(col.field)?.root;
1,224,553✔
605
    }
606

607
    /**
608
     * Spawns the add row UI for the specific row.
609
     *
610
     * @example
611
     * ```typescript
612
     * const row = this.grid1.getRowByIndex(1);
613
     * row.beginAddRow();
614
     * ```
615
     */
616
    public beginAddRow() {
617
        this.grid.crudService.enterAddRowMode(this);
44✔
618
    }
619

620
    /**
621
     * @hidden
622
     */
623
    public triggerAddAnimation() {
624
        this.triggerAddAnimationClass = true;
59✔
625
    }
626

627
    /**
628
     * @hidden
629
     */
630
    public animationEndHandler() {
631
        this.triggerAddAnimationClass = false;
44✔
632
        this.addAnimationEnd.emit(this);
44✔
633
    }
634

635
    protected getMergeCellSpan(col: ColumnType) {
636
        const rowCount = this.metaData.cellMergeMeta.get(col.field).rowSpan;
535✔
637
        let sizeSpans = "";
535✔
638
        const isPinned = this.pinned && !this.disabled;
535✔
639
        const indexInData = this.grid.isRowPinningToTop && !isPinned ? this.index - this.grid.pinnedRecordsCount : this.index;
535✔
640
        for (let index = indexInData; index < indexInData + rowCount; index++) {
535✔
641
            const size = this.grid.verticalScrollContainer.getSizeAt(index);
1,103✔
642
            sizeSpans += size + 'px ';
1,103✔
643
        }
644
        return `${sizeSpans}`;
535✔
645
    }
646

647
    protected isSelectionRoot(col: ColumnType) {
648
        const mergeMeta = this.metaData?.cellMergeMeta;
1,125,257✔
649
        const rowCount = mergeMeta?.get(col.field)?.rowSpan;
1,125,257✔
650
        if (mergeMeta && rowCount > 1) {
1,125,257✔
651
            const isPinned = this.pinned && !this.disabled;
535✔
652
            const indexInData = this.grid.isRowPinningToTop && !isPinned ? this.index - this.grid.pinnedRecordsCount : this.index;
535✔
653
            const range = isPinned ? this.grid.pinnedDataView.slice(indexInData, indexInData + rowCount) : this.grid.verticalScrollContainer.igxForOf.slice(indexInData, indexInData + rowCount);
535✔
654
            const inRange = range.filter(x => this.selectionService.isRowSelected(this.extractRecordKey(x))).length > 0;
1,103✔
655
            return inRange;
535✔
656
        }
657
        return false;
1,124,722✔
658
    }
659

660
    protected isHoveredRoot(col: ColumnType) {
661
        const mergeMeta = this.metaData?.cellMergeMeta;
1,125,257✔
662
        const rowCount = mergeMeta?.get(col.field)?.rowSpan;
1,125,257✔
663
        if (mergeMeta && rowCount > 1 && this.grid.hoverIndex !== null && this.grid.hoverIndex !== undefined) {
1,125,257✔
664
            const indexInData = this.index;
3✔
665
            const hoveredIndex = this.grid.hoverIndex;
3✔
666
            return indexInData <= hoveredIndex && indexInData + rowCount > hoveredIndex;
3✔
667
        }
668
        return false;
1,125,254✔
669
    }
670

671
    protected extractRecordKey(rec: any) {
672
        let recData = rec;
1,103✔
673
        if (this.grid.isRecordMerged(recData)) {
1,103✔
674
            recData = rec.recordRef;
1,069✔
675
        }
676

677
        if(this.grid.isTreeRow && this.grid.isTreeRow(recData)){
1,103✔
678
            recData = recData.data;
14✔
679
        }
680
        return this.grid.primaryKey ? recData[this.grid.primaryKey] : recData;
1,103✔
681
    }
682

683
    protected getRowHeight() {
684
        const isPinned = this.pinned && !this.disabled;
535✔
685
        const indexInData = this.grid.isRowPinningToTop && !isPinned ? this.index - this.grid.pinnedRecordsCount : this.index;
535✔
686
        if ((this.grid as any)._cdrRequests) {
535✔
687
            // recalc size if repaint is requested.
688
            this.grid.verticalScrollContainer.recalcUpdateSizes();
367✔
689
        }
690
        const size = this.grid.verticalScrollContainer.getSizeAt(indexInData) - 1;
535✔
691
        return size || this.grid.rowHeight;
535!
692
    }
693

694
    /**
695
     * @hidden
696
     */
697
    public get resolveDragIndicatorClasses(): string {
698
        const defaultDragIndicatorCssClass = 'igx-grid__drag-indicator';
1,987✔
699
        const dragIndicatorOff = this.grid.rowDragging && !this.dragging ? 'igx-grid__drag-indicator--off' : '';
1,987✔
700
        return `${defaultDragIndicatorCssClass} ${dragIndicatorOff}`;
1,987✔
701
    }
702

703
    /**
704
     * - state persistence switching all pinned columns resets collection
705
     * - MRL unpinnedColumns igxFor modes entire child loop on unpin
706
     */
707
    protected trackPinnedColumn = trackByIdentity;
30,690✔
708
}
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