• 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

46.72
/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 { IgxAddRow, 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

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

41
    /**
42
     * @hidden
43
     */
44
    @HostBinding('attr.role')
45
    public role = 'row';
265✔
46

47
    /**
48
     *  The data passed to the row component.
49
     *
50
     * ```typescript
51
     * // get the row data for the first selected row
52
     * let selectedRowData = this.grid.selectedRows[0].data;
53
     * ```
54
     */
55
    @Input()
56
    public get data(): any {
57
        if (this.inEditMode) {
79,770!
UNCOV
58
            return mergeWith(this.grid.dataCloneStrategy.clone(this._data), this.grid.transactions.getAggregatedValue(this.key, false),
×
59
                (objValue, srcValue) => {
UNCOV
60
                    if (Array.isArray(srcValue)) {
×
61
                        return objValue = srcValue;
×
62
                    }
63
                });
64
        }
65
        return this._data;
79,770✔
66
    }
67

68
    public set data(v: any) {
69
        this._data = v;
296✔
70
    }
71
    /**
72
     * The index of the row.
73
     *
74
     * ```typescript
75
     * // get the index of the second selected row
76
     * let selectedRowIndex = this.grid.selectedRows[1].index;
77
     * ```
78
     */
79
    @Input()
80
    public index: number;
81

82
    /**
83
     * Sets whether this specific row has disabled functionality for editing and row selection.
84
     * Default value is `false`.
85
     * ```typescript
86
     * this.grid.selectedRows[0].pinned = true;
87
     * ```
88
     */
89
    @Input({ transform: booleanAttribute })
90
    @HostBinding('attr.aria-disabled')
91
    @HostBinding('class.igx-grid__tr--disabled')
92
    public disabled = false;
265✔
93

94
    /**
95
     * Sets whether the row is pinned.
96
     * Default value is `false`.
97
     * ```typescript
98
     * this.grid.selectedRows[0].pinned = true;
99
     * ```
100
     */
101
    public set pinned(value: boolean) {
UNCOV
102
        if (value) {
×
UNCOV
103
            this.grid.pinRow(this.key);
×
104
        } else {
UNCOV
105
            this.grid.unpinRow(this.key);
×
106
        }
107
    }
108

109
    /**
110
     * Gets whether the row is pinned.
111
     * ```typescript
112
     * let isPinned = row.pinned;
113
     * ```
114
     */
115
    public get pinned(): boolean {
116
        return this.grid.isRecordPinned(this.data);
10,100✔
117
    }
118

119
    /**
120
     * Gets the expanded state of the row.
121
     * ```typescript
122
     * let isExpanded = row.expanded;
123
     * ```
124
     */
125
    public get expanded(): boolean {
UNCOV
126
        return this.grid.gridAPI.get_row_expansion_state(this.data);
×
127
    }
128

129
    /**
130
     * Expands/collapses the current row.
131
     *
132
     * ```typescript
133
     * this.grid.selectedRows[2].expanded = true;
134
     * ```
135
     */
136
    public set expanded(val: boolean) {
137
        this.grid.gridAPI.set_row_expansion_state(this.key, val);
×
138
    }
139

140
    public get addRowUI(): any {
141
        return !!this.grid.crudService.row &&
12,867!
142
            this.grid.crudService.row.getClassName() === IgxAddRow.name &&
143
            this.grid.crudService.row.id === this.key;
144
    }
145

146
    @HostBinding('style.min-height.px')
147
    public get rowHeight() {
148
        let height = this.grid.rowHeight || 32;
1,442!
149
        if (this.grid.hasColumnLayouts) {
1,442!
UNCOV
150
            const maxRowSpan = this.grid.multiRowLayoutRowSize;
×
UNCOV
151
            height = height * maxRowSpan;
×
152
        }
153
        return this.addRowUI ? height : null;
1,442!
154
    }
155

156
    /**
157
     * @hidden
158
     */
159
    @Input()
160
    public gridID: string;
161

162
    /**
163
     * @hidden
164
     */
165
    @ViewChildren('igxDirRef', { read: IgxGridForOfDirective })
166
    public _virtDirRow: QueryList<IgxGridForOfDirective<ColumnType, ColumnType[]>>;
167

168
    /* blazorSuppress */
169
    public get virtDirRow(): IgxGridForOfDirective<ColumnType, ColumnType[]> {
170
        return this._virtDirRow ? this._virtDirRow.first : null;
623!
171
    }
172

173
    /**
174
     * @hidden
175
     */
176
    @ViewChild(forwardRef(() => IgxCheckboxComponent), { read: IgxCheckboxComponent })
1✔
177
    public checkboxElement: IgxCheckboxComponent;
178

179
    @ViewChildren('cell')
180
    protected _cells: QueryList<CellType>;
181

182
    /**
183
     * Gets the rendered cells in the row component.
184
     *
185
     * ```typescript
186
     * // get the cells of the third selected row
187
     * let selectedRowCells = this.grid.selectedRows[2].cells;
188
     * ```
189
     */
190
    public get cells() {
191
        const res = new QueryList<CellType>();
29✔
192
        if (!this._cells) {
29!
193
            return res;
×
194
        }
195
        const cList = this._cells.filter((item) => item.nativeElement.parentElement !== null)
198✔
196
            .sort((item1, item2) => item1.column.visibleIndex - item2.column.visibleIndex);
169✔
197
        res.reset(cList);
29✔
198
        return res;
29✔
199
    }
200

201
    @HostBinding('attr.data-rowIndex')
202
    public get dataRowIndex() {
203
        return this.index;
1,442✔
204
    }
205

206
    /**
207
     * @hidden
208
     */
209
    @Input()
210
    @HostBinding('attr.aria-selected')
211
    public get selected(): boolean {
212
        return this.selectionService.isRowSelected(this.key);
12,822✔
213
    }
214

215
    public set selected(value: boolean) {
UNCOV
216
        if (value) {
×
UNCOV
217
            this.selectionService.selectRowsWithNoEvent([this.key]);
×
218
        } else {
UNCOV
219
            this.selectionService.deselectRowsWithNoEvent([this.key]);
×
220
        }
UNCOV
221
        this.grid.cdr.markForCheck();
×
222
    }
223

224
    /**
225
     * @hidden
226
     */
227
    public get columns(): ColumnType[] {
228
        return this.grid.visibleColumns;
×
229
    }
230

231
    /**
232
     * @hidden
233
     * @internal
234
     */
235
    public get viewIndex(): number {
236
        if ((this.grid as any).groupingExpressions.length) {
19,552!
UNCOV
237
            return this.grid.filteredSortedData.indexOf(this.data);
×
238
        }
239
        return this.index + this.grid.page * this.grid.perPage;
19,552✔
240
    }
241

242
    /**
243
     * @hidden
244
     */
245
    public get pinnedColumns(): ColumnType[] {
246
        return this.grid.pinnedColumns;
2,974✔
247
    }
248

249
    /**
250
     * @hidden
251
     */
252
    public get isRoot(): boolean {
UNCOV
253
        return true;
×
254
    }
255

256
    /**
257
     * @hidden
258
     */
259
    public get hasChildren(): boolean {
260
        return false;
×
261
    }
262

263
    /**
264
     * @hidden
265
     */
266
    public get unpinnedColumns(): ColumnType[] {
267
        return this.grid.unpinnedColumns;
1,487✔
268
    }
269

270
    /**
271
     * @hidden
272
     */
273
    public get showRowSelectors(): boolean {
274
        return this.grid.showRowSelectors;
1,487✔
275
    }
276

277
    /** @hidden */
278
    public get dirty(): boolean {
279
        const row = this.grid.transactions.getState(this.key);
1,442✔
280
        if (row) {
1,442!
UNCOV
281
            return row.type === TransactionType.ADD || row.type === TransactionType.UPDATE;
×
282
        }
283

284
        return false;
1,442✔
285
    }
286

287
    /**
288
     * @hidden
289
     */
290
    public get rowDraggable(): boolean {
291
        return this.grid.rowDraggable;
1,487✔
292
    }
293

294
    /** @hidden */
295
    public get added(): boolean {
296
        const row = this.grid.transactions.getState(this.key);
54✔
297
        if (row) {
54!
UNCOV
298
            return row.type === TransactionType.ADD;
×
299
        }
300

301
        return false;
54✔
302
    }
303

304
    /** @hidden */
305
    public get deleted(): boolean {
306
        return this.grid.gridAPI.row_deleted_transaction(this.key);
37,526✔
307
    }
308

309
    /**
310
     * @hidden
311
     */
312
    public get dragging() {
313
        return this.grid.dragRowID === this.key;
1,442✔
314
    }
315

316
    // TODO: Refactor
317
    public get inEditMode(): boolean {
318
        if (this.grid.rowEditable) {
81,212!
UNCOV
319
            const editRowState = this.grid.crudService.row;
×
UNCOV
320
            return (editRowState && editRowState.id === this.key) || false;
×
321
        } else {
322
            return false;
81,212✔
323
        }
324
    }
325

326
    /**
327
     * Gets the ID of the row.
328
     * A row in the grid is identified either by:
329
     * - primaryKey data value,
330
     * - the whole data, if the primaryKey is omitted.
331
     *
332
     * ```typescript
333
     * let rowID = this.grid.selectedRows[2].key;
334
     * ```
335
     */
336
    public get key() {
337
        const primaryKey = this.grid.primaryKey;
109,296✔
338
        if (this._data) {
109,296!
339
            return primaryKey ? this._data[primaryKey] : this._data;
109,296✔
340
        } else {
341
            return undefined;
×
342
        }
343
    }
344

345
    /**
346
     * The native DOM element representing the row. Could be null in certain environments.
347
     *
348
     * ```typescript
349
     * // get the nativeElement of the second selected row
350
     * let selectedRowNativeElement = this.grid.selectedRows[1].nativeElement;
351
     * ```
352
     */
353
    public get nativeElement() {
354
        return this.element.nativeElement;
443✔
355
    }
356

357
    /**
358
     * @hidden
359
     */
360
    public focused = false;
265✔
361

362
    /**
363
     * @hidden
364
     * @internal
365
     */
366
    public defaultCssClass = 'igx-grid__tr';
265✔
367

368
    /**
369
     * @hidden
370
     */
371
    public triggerAddAnimationClass = false;
265✔
372

373
    protected destroy$ = new Subject<any>();
265✔
374
    protected _data: any;
375
    protected _addRow: boolean;
376

377
    constructor(
378
        @Inject(IGX_GRID_BASE) public grid: GridType,
265✔
379
        public selectionService: IgxGridSelectionService,
265✔
380
        public element: ElementRef<HTMLElement>,
265✔
381
        public cdr: ChangeDetectorRef) { }
265✔
382

383
    /**
384
     * @hidden
385
     * @internal
386
     */
387
    @HostListener('click', ['$event'])
388
    public onClick(event: MouseEvent) {
UNCOV
389
        this.grid.rowClick.emit({
×
390
            row: this,
391
            event
392
        });
393

UNCOV
394
        if (this.grid.rowSelection === 'none' || this.deleted || !this.grid.selectRowOnClick) {
×
UNCOV
395
            return;
×
396
        }
UNCOV
397
        if (event.shiftKey && this.grid.isMultiRowSelectionEnabled) {
×
UNCOV
398
            this.selectionService.selectMultipleRows(this.key, this.data, event);
×
UNCOV
399
            return;
×
400
        }
401

UNCOV
402
        const clearSelection = !(+event.ctrlKey ^ +event.metaKey);
×
UNCOV
403
        if (this.selected && !clearSelection) {
×
UNCOV
404
            this.selectionService.deselectRow(this.key, event);
×
405
        } else {
UNCOV
406
            this.selectionService.selectRowById(this.key, clearSelection, event);
×
407
        }
408
    }
409

410
    /**
411
     * @hidden
412
     * @internal
413
     */
414
    @HostListener('contextmenu', ['$event'])
415
    public onContextMenu(event: MouseEvent) {
UNCOV
416
        const cell = (event.target as HTMLElement).closest('.igx-grid__td');
×
UNCOV
417
        this.grid.contextMenu.emit({
×
418
            row: this,
UNCOV
419
            cell: this.cells.find(c => c.nativeElement === cell),
×
420
            event
421
        });
422
    }
423

424
    /**
425
     * @hidden
426
     * @internal
427
     */
428
    @HostListener('mouseenter')
429
    public showActionStrip() {
UNCOV
430
        if (this.grid.actionStrip) {
×
UNCOV
431
            this.grid.actionStrip.show(this);
×
432
        }
433
    }
434

435
    /**
436
     * @hidden
437
     * @internal
438
     */
439
    @HostListener('mouseleave')
440
    public hideActionStrip() {
UNCOV
441
        if (this.grid.actionStrip && this.grid.actionStrip.hideOnRowLeave) {
×
UNCOV
442
            this.grid.actionStrip.hide();
×
443
        }
444
    }
445

446
    /**
447
     * @hidden
448
     * @internal
449
     */
450
    public ngAfterViewInit() {
451
        // 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.
452
        this._virtDirRow.changes.pipe(takeUntil(this.destroy$)).subscribe(() => this.grid.resetHorizontalVirtualization());
265✔
453
    }
454

455
    /**
456
     * @hidden
457
     * @internal
458
     */
459
    public ngOnDestroy() {
460
        this.destroy$.next(true);
265✔
461
        this.destroy$.complete();
265✔
462
    }
463

464
    /**
465
     * @hidden
466
     */
467
    public onRowSelectorClick(event) {
UNCOV
468
        event.stopPropagation();
×
UNCOV
469
        if (event.shiftKey && this.grid.isMultiRowSelectionEnabled) {
×
470
            this.selectionService.selectMultipleRows(this.key, this.data, event);
×
471
            return;
×
472
        }
UNCOV
473
        if (this.selected) {
×
UNCOV
474
            this.selectionService.deselectRow(this.key, event);
×
475
        } else {
UNCOV
476
            this.selectionService.selectRowById(this.key, false, event);
×
477
        }
478
    }
479

480
    /**
481
     * Updates the specified row object and the data source record with the passed value.
482
     *
483
     * ```typescript
484
     * // update the second selected row's value
485
     * let newValue = "Apple";
486
     * this.grid.selectedRows[1].update(newValue);
487
     * ```
488
     */
489
    public update(value: any) {
UNCOV
490
        const crudService = this.grid.crudService;
×
UNCOV
491
        if (crudService.cellInEditMode && crudService.cell.id.key === this.key) {
×
492
            this.grid.transactions.endPending(false);
×
493
        }
UNCOV
494
        const row = new IgxEditRow(this.key, this.index, this.data, this.grid);
×
UNCOV
495
        this.grid.gridAPI.update_row(row, value);
×
UNCOV
496
        this.cdr.markForCheck();
×
497
    }
498

499
    /**
500
     * Removes the specified row from the grid's data source.
501
     * This method emits `rowDeleted` event.
502
     *
503
     * ```typescript
504
     * // delete the third selected row from the grid
505
     * this.grid.selectedRows[2].delete();
506
     * ```
507
     */
508
    public delete() {
UNCOV
509
        this.grid.deleteRowById(this.key);
×
510
    }
511

512
    public isCellActive(visibleColumnIndex) {
513
        const node = this.grid.navigation.activeNode;
9,938✔
514
        return node ? node.row === this.index && node.column === visibleColumnIndex : false;
9,938!
515
    }
516

517
    /**
518
     * Pins the specified row.
519
     * This method emits `rowPinning`\`rowPinned` event.
520
     *
521
     * ```typescript
522
     * // pin the selected row from the grid
523
     * this.grid.selectedRows[0].pin();
524
     * ```
525
     */
526
    public pin() {
UNCOV
527
        return this.grid.pinRow(this.key);
×
528
    }
529

530
    /**
531
     * Unpins the specified row.
532
     * This method emits `rowPinning`\`rowPinned` event.
533
     *
534
     * ```typescript
535
     * // unpin the selected row from the grid
536
     * this.grid.selectedRows[0].unpin();
537
     * ```
538
     */
539
    public unpin() {
UNCOV
540
        return this.grid.unpinRow(this.key);
×
541
    }
542

543
    /**
544
     * @hidden
545
     */
546
    public get rowCheckboxAriaLabel() {
UNCOV
547
        return this.grid.primaryKey ?
×
548
            this.selected ? 'Deselect row with key ' + this.key : 'Select row with key ' + this.key :
×
549
            this.selected ? 'Deselect row' : 'Select row';
×
550
    }
551

552
    /**
553
     * @hidden
554
     */
555
    public ngDoCheck() {
556
        this.cdr.markForCheck();
1,442✔
557
    }
558

559
    /**
560
     * @hidden
561
     */
562
    public shouldDisplayPinnedChip(visibleColumnIndex: number): boolean {
563
        return this.pinned && this.disabled && visibleColumnIndex === 0;
9,938!
564
    }
565

566
    /**
567
     * Spawns the add row UI for the specific row.
568
     *
569
     * @example
570
     * ```typescript
571
     * const row = this.grid1.getRowByIndex(1);
572
     * row.beginAddRow();
573
     * ```
574
     */
575
    public beginAddRow() {
UNCOV
576
        this.grid.crudService.enterAddRowMode(this);
×
577
    }
578

579
    /**
580
     * @hidden
581
     */
582
    public triggerAddAnimation() {
UNCOV
583
        this.triggerAddAnimationClass = true;
×
584
    }
585

586
    /**
587
     * @hidden
588
     */
589
    public animationEndHandler() {
UNCOV
590
        this.triggerAddAnimationClass = false;
×
UNCOV
591
        this.addAnimationEnd.emit(this);
×
592
    }
593

594
    /**
595
     * @hidden
596
     */
597
    public get resolveDragIndicatorClasses(): string {
UNCOV
598
        const defaultDragIndicatorCssClass = 'igx-grid__drag-indicator';
×
UNCOV
599
        const dragIndicatorOff = this.grid.rowDragging && !this.dragging ? 'igx-grid__drag-indicator--off' : '';
×
UNCOV
600
        return `${defaultDragIndicatorCssClass} ${dragIndicatorOff}`;
×
601
    }
602
}
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