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

IgniteUI / igniteui-angular / 26023601418

18 May 2026 08:57AM UTC coverage: 4.854% (-85.3%) from 90.174%
26023601418

Pull #17281

github

web-flow
Merge e7ce7a18e into 5a85df190
Pull Request #17281: feat: Added virtual scroll component and sample implementation

400 of 17347 branches covered (2.31%)

Branch coverage included in aggregate %.

63 of 222 new or added lines in 4 files covered. (28.38%)

27932 existing lines in 341 files now uncovered.

2022 of 32547 relevant lines covered (6.21%)

0.72 hits per line

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

0.22
/projects/igniteui-angular/grids/core/src/api.service.ts
1
import { inject, Injectable } from '@angular/core';
2
import { Subject } from 'rxjs';
3
import {
4
    cloneArray,
5
    reverseMapper,
6
    mergeObjects,
7
    Transaction,
8
    TransactionType,
9
    State,
10
    DataUtil,
11
    FilterUtil,
12
    GridColumnDataType,
13
    IFilteringExpressionsTree,
14
    IGroupingExpression,
15
    ISortingExpression,
16
    SortingDirection,
17
    ColumnType
18
} from 'igniteui-angular/core';
19
import { IgxCell, IgxGridCRUDService, IgxEditRow } from './common/crud.service';
20
import { CellType, GridServiceType, GridType, RowType } from './common/grid.interface';
21
import { IGridEditEventArgs, IPinRowEventArgs, IRowToggleEventArgs } from './common/events';
22
import { IgxColumnMovingService } from './moving/moving.service';
23

24
/**
25
 * @hidden
26
 */
27
@Injectable()
28
export class GridBaseAPIService<T extends GridType> implements GridServiceType {
3✔
29

UNCOV
30
    public crudService = inject(IgxGridCRUDService);
×
UNCOV
31
    public cms = inject(IgxColumnMovingService)
×
32

33
    public grid: T;
UNCOV
34
    protected destroyMap: Map<string, Subject<boolean>> = new Map<string, Subject<boolean>>();
×
35

36
    public get_column_by_name(name: string): ColumnType {
UNCOV
37
        return this.grid.columns.find((col: ColumnType) => col.field === name);
×
38
    }
39

40
    public get_summary_data(): any[] | null {
UNCOV
41
        const grid = this.grid;
×
UNCOV
42
        let data = grid.filteredData;
×
UNCOV
43
        if (data && grid.hasPinnedRecords) {
×
UNCOV
44
            data = grid._filteredUnpinnedData;
×
45
        }
UNCOV
46
        if (!data) {
×
UNCOV
47
            if (grid.transactions.enabled) {
×
UNCOV
48
                data = DataUtil.mergeTransactions(
×
49
                    cloneArray(grid.data),
50
                    grid.transactions.getAggregatedChanges(true),
51
                    grid.primaryKey,
52
                    grid.dataCloneStrategy
53
                );
UNCOV
54
                const deletedRows = grid.transactions.getTransactionLog().filter(t => t.type === TransactionType.DELETE).map(t => t.id);
×
UNCOV
55
                deletedRows.forEach(rowID => {
×
UNCOV
56
                    const tempData = grid.primaryKey ? data.map(rec => rec[grid.primaryKey]) : data;
×
UNCOV
57
                    const index = tempData.indexOf(rowID);
×
UNCOV
58
                    if (index !== -1) {
×
UNCOV
59
                        data.splice(index, 1);
×
60
                    }
61
                });
62
            } else {
UNCOV
63
                data = grid.data;
×
64
            }
65
        }
UNCOV
66
        return data;
×
67
    }
68

69
    /**
70
     * @hidden
71
     * @internal
72
     */
73
    public getRowData(rowID: any) {
UNCOV
74
        const data = this.get_all_data(this.grid.transactions.enabled);
×
UNCOV
75
        const index = this.get_row_index_in_data(rowID, data);
×
UNCOV
76
        return data[index];
×
77
    }
78

79
    public get_row_index_in_data(rowID: any, dataCollection?: any[]): number {
UNCOV
80
        const grid = this.grid;
×
UNCOV
81
        if (!grid) {
×
82
            return -1;
×
83
        }
UNCOV
84
        const data = dataCollection ?? this.get_all_data(grid.transactions.enabled);
×
UNCOV
85
        return grid.primaryKey ? data.findIndex(record => record.recordRef ? record.recordRef[grid.primaryKey] === rowID
×
86
            : record[grid.primaryKey] === rowID) : data.indexOf(rowID);
87
    }
88

89
    public get_row_by_key(rowSelector: any): RowType {
UNCOV
90
        if (!this.grid) {
×
91
            return null;
×
92
        }
UNCOV
93
        const primaryKey = this.grid.primaryKey;
×
UNCOV
94
        if (primaryKey !== undefined && primaryKey !== null) {
×
UNCOV
95
            return this.grid.dataRowList.find((row) => row.data[primaryKey] === rowSelector);
×
96
        } else {
UNCOV
97
            return this.grid.dataRowList.find((row) => row.data === rowSelector);
×
98
        }
99
    }
100

101
    public get_row_by_index(rowIndex: number): RowType {
UNCOV
102
        return this.grid.rowList.find((row) => row.index === rowIndex);
×
103
    }
104

105
    /**
106
     * Gets the rowID of the record at the specified data view index
107
     *
108
     * @param index
109
     * @param dataCollection
110
     */
111
    public get_rec_id_by_index(index: number, dataCollection?: any[]): any {
112
        dataCollection = dataCollection || this.grid.data;
×
113
        if (index >= 0 && index < dataCollection.length) {
×
114
            const rec = dataCollection[index];
×
115
            return this.grid.primaryKey ? rec[this.grid.primaryKey] : rec;
×
116
        }
117
        return null;
×
118
    }
119

120
    public get_cell_by_key(rowSelector: any, field: string): CellType {
UNCOV
121
        const row = this.get_row_by_key(rowSelector);
×
UNCOV
122
        if (row && row.cells) {
×
UNCOV
123
            return row.cells.find((cell) => cell.column.field === field);
×
124
        }
125
    }
126

127
    public get_cell_by_index(rowIndex: number, columnID: number | string): CellType {
UNCOV
128
        const row = this.get_row_by_index(rowIndex);
×
UNCOV
129
        const hasCells = row && row.cells;
×
UNCOV
130
        if (hasCells && typeof columnID === 'number') {
×
UNCOV
131
            return row.cells.find((cell) => cell.column.index === columnID);
×
132
        }
UNCOV
133
        if (hasCells && typeof columnID === 'string') {
×
UNCOV
134
            return row.cells.find((cell) => cell.column.field === columnID);
×
135
        }
136

137
    }
138

139
    public get_cell_by_visible_index(rowIndex: number, columnIndex: number): CellType {
UNCOV
140
        const row = this.get_row_by_index(rowIndex);
×
UNCOV
141
        if (row && row.cells) {
×
UNCOV
142
            return row.cells.find((cell) => cell.visibleColumnIndex === columnIndex);
×
143
        }
144
    }
145

146
    public update_cell(cell: IgxCell): IGridEditEventArgs {
UNCOV
147
        if (!cell) {
×
UNCOV
148
            return;
×
149
        }
UNCOV
150
        const args = cell.createCellEditEventArgs(true);
×
UNCOV
151
        if (!this.grid.crudService.row) { // should not recalculate summaries when there is row in edit mode
×
UNCOV
152
            this.grid.summaryService.clearSummaryCache(args);
×
153
        }
UNCOV
154
        const data = this.getRowData(cell.id.rowID);
×
UNCOV
155
        const newRowData = reverseMapper(cell.column.field, args.newValue);
×
UNCOV
156
        this.updateData(this.grid, cell.id.rowID, data, cell.rowData, newRowData);
×
UNCOV
157
        if (!this.grid.crudService.row) {
×
UNCOV
158
            this.grid.validation.update(cell.id.rowID, newRowData);
×
159
        }
UNCOV
160
        if (this.grid.primaryKey === cell.column.field) {
×
UNCOV
161
            if (this.grid.pinnedRecords.length > 0) {
×
UNCOV
162
                const rowIndex = this.grid.pinnedRecords.indexOf(cell.rowData);
×
UNCOV
163
                if (rowIndex !== -1) {
×
UNCOV
164
                    const previousRowId = cell.value;
×
UNCOV
165
                    const rowType = this.grid.getRowByIndex(cell.rowIndex);
×
UNCOV
166
                    this.unpin_row(previousRowId, rowType);
×
UNCOV
167
                    this.pin_row(args.newValue, rowIndex, rowType);
×
168
                }
169
            }
UNCOV
170
            if (this.grid.selectionService.isRowSelected(cell.id.rowID)) {
×
UNCOV
171
                this.grid.selectionService.deselectRow(cell.id.rowID);
×
UNCOV
172
                this.grid.selectionService.selectRowById(args.newValue);
×
173
            }
UNCOV
174
            if (this.grid.hasSummarizedColumns) {
×
175
                this.grid.summaryService.removeSummaries(cell.id.rowID);
×
176
            }
177
        }
UNCOV
178
        if (!this.grid.rowEditable || !this.crudService.row ||
×
179
            this.crudService.row.id !== cell.id.rowID || !this.grid.transactions.enabled) {
UNCOV
180
            this.grid.summaryService.clearSummaryCache(args);
×
UNCOV
181
            this.grid.pipeTrigger++;
×
182
        }
183

UNCOV
184
        return args;
×
185
    }
186

187
    // TODO: CRUD refactor to not emit editing evts.
188
    public update_row(row: IgxEditRow, value: any, event?: Event) {
UNCOV
189
        const grid = this.grid;
×
UNCOV
190
        const selected = grid.selectionService.isRowSelected(row.id);
×
UNCOV
191
        const rowInEditMode = this.crudService.row;
×
UNCOV
192
        const data = this.get_all_data(grid.transactions.enabled);
×
UNCOV
193
        const index = this.get_row_index_in_data(row.id, data);
×
UNCOV
194
        const hasSummarized = grid.hasSummarizedColumns;
×
UNCOV
195
        this.crudService.updateRowEditData(row, value);
×
196

UNCOV
197
        const args = row.createRowEditEventArgs(true, event);
×
198

199
        // If no valid row is found
UNCOV
200
        if (index === -1) {
×
UNCOV
201
            return args;
×
202
        }
203

UNCOV
204
        if (rowInEditMode) {
×
UNCOV
205
            const hasChanges = grid.transactions.getState(args.rowID, true);
×
UNCOV
206
            grid.transactions.endPending(false);
×
UNCOV
207
            if (!hasChanges) {
×
UNCOV
208
                return args;
×
209
            }
210
        }
211

UNCOV
212
        if (!args.newValue) {
×
213
            return args;
×
214
        }
215

UNCOV
216
        if (hasSummarized) {
×
UNCOV
217
            grid.summaryService.removeSummaries(args.rowID);
×
218
        }
219

UNCOV
220
        this.updateData(grid, row.id, data[index], args.oldValue, args.newValue);
×
UNCOV
221
        this.grid.validation.update(row.id, args.newValue);
×
UNCOV
222
        const newId = grid.primaryKey ? args.newValue[grid.primaryKey] : args.newValue;
×
UNCOV
223
        if (selected) {
×
UNCOV
224
            grid.selectionService.deselectRow(row.id);
×
UNCOV
225
            grid.selectionService.selectRowById(newId);
×
226
        }
227
        // make sure selection is handled prior to updating the row.id
UNCOV
228
        row.id = newId;
×
UNCOV
229
        if (hasSummarized) {
×
UNCOV
230
            grid.summaryService.removeSummaries(newId);
×
231
        }
UNCOV
232
        grid.pipeTrigger++;
×
233

UNCOV
234
        return args;
×
235
    }
236

237
    public sort(expression: ISortingExpression): void {
UNCOV
238
        if (expression.dir === SortingDirection.None) {
×
UNCOV
239
            this.remove_grouping_expression(expression.fieldName);
×
240
        }
UNCOV
241
        const sortingState = cloneArray(this.grid.sortingExpressions);
×
UNCOV
242
        this.prepare_sorting_expression([sortingState], expression);
×
UNCOV
243
        this.grid.sortingExpressions = sortingState;
×
244
    }
245

246
    public sort_decoupled(expression: IGroupingExpression): void {
247
        if (expression.dir === SortingDirection.None) {
×
248
            this.remove_grouping_expression(expression.fieldName);
×
249
        }
250
        const groupingState = cloneArray((this.grid as any).groupingExpressions);
×
251
        this.prepare_grouping_expression([groupingState], expression);
×
252
        (this.grid as any).groupingExpressions = groupingState;
×
253
    }
254

255
    public sort_multiple(expressions: ISortingExpression[]): void {
UNCOV
256
        const sortingState = cloneArray(this.grid.sortingExpressions);
×
257

UNCOV
258
        for (const each of expressions) {
×
UNCOV
259
            if (each.dir === SortingDirection.None) {
×
260
                this.remove_grouping_expression(each.fieldName);
×
261
            }
UNCOV
262
            this.prepare_sorting_expression([sortingState], each);
×
263
        }
264

UNCOV
265
        this.grid.sortingExpressions = sortingState;
×
266
    }
267

268
    public sort_groupBy_multiple(expressions: ISortingExpression[]): void {
UNCOV
269
        const groupingState = cloneArray((this.grid as any).groupingExpressions);
×
270

UNCOV
271
        for (const each of expressions) {
×
UNCOV
272
            if (each.dir === SortingDirection.None) {
×
273
                this.remove_grouping_expression(each.fieldName);
×
274
            }
UNCOV
275
            this.prepare_grouping_expression([groupingState], each);
×
276
        }
277
    }
278

279
    public clear_sort(fieldName: string) {
UNCOV
280
        const sortingState = this.grid.sortingExpressions;
×
UNCOV
281
        const index = sortingState.findIndex((expr) => expr.fieldName === fieldName);
×
UNCOV
282
        if (index > -1) {
×
UNCOV
283
            sortingState.splice(index, 1);
×
UNCOV
284
            this.grid.sortingExpressions = sortingState;
×
285
        }
286
    }
287

288
    public clear_groupby(_name?: string | Array<string>) {
289
    }
290

291
    public should_apply_number_style(column: ColumnType): boolean {
292
        return column.dataType === GridColumnDataType.Number
×
293
            || column.dataType === GridColumnDataType.Currency
294
            || column.dataType === GridColumnDataType.Percent;
295
    }
296

297
    public get_data(): any[] {
UNCOV
298
        const grid = this.grid;
×
UNCOV
299
        const data = grid.data ? grid.data : [];
×
UNCOV
300
        return data;
×
301
    }
302

303
    public get_all_data(includeTransactions = false): any[] {
×
UNCOV
304
        const grid = this.grid;
×
UNCOV
305
        let data = grid && grid.data ? grid.data : [];
×
UNCOV
306
        data = includeTransactions ? grid.dataWithAddedInTransactionRows : data;
×
UNCOV
307
        return data;
×
308
    }
309

310
    public get_filtered_data(): any[] {
311
        return this.grid.filteredData;
×
312
    }
313

314
    public addRowToData(rowData: any, _parentID?: any) {
315
        // Add row goes to transactions and if rowEditable is properly implemented, added rows will go to pending transactions
316
        // If there is a row in edit - > commit and close
UNCOV
317
        const grid = this.grid;
×
UNCOV
318
        const rowId = grid.primaryKey ? rowData[grid.primaryKey] : rowData;
×
UNCOV
319
        if (grid.transactions.enabled) {
×
UNCOV
320
            const transaction: Transaction = { id: rowId, type: TransactionType.ADD, newValue: rowData };
×
UNCOV
321
            grid.transactions.add(transaction);
×
322
        } else {
UNCOV
323
            grid.data.push(rowData);
×
UNCOV
324
            grid.data = cloneArray(grid.data);
×
325
        }
UNCOV
326
        grid.validation.markAsTouched(rowId);
×
UNCOV
327
        grid.validation.update(rowId, rowData);
×
328
    }
329

330
    public deleteRowFromData(rowID: any, index: number) {
331
        //  if there is a row (index !== 0) delete it
332
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
UNCOV
333
        const grid = this.grid;
×
UNCOV
334
        if (index !== -1) {
×
UNCOV
335
            if (grid.transactions.enabled) {
×
UNCOV
336
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
UNCOV
337
                grid.transactions.add(transaction, grid.data[index]);
×
338
            } else {
UNCOV
339
                grid.data.splice(index, 1);
×
UNCOV
340
                grid.data = cloneArray(grid.data);
×
341
            }
342
        } else {
UNCOV
343
            const state: State = grid.transactions.getState(rowID);
×
UNCOV
344
            grid.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
345
        }
UNCOV
346
        grid.validation.clear(rowID);
×
347
    }
348

349
    public deleteRowById(rowId: any): any {
350
        let index: number;
UNCOV
351
        const grid = this.grid;
×
UNCOV
352
        const data = this.get_all_data(grid.transactions.enabled);
×
UNCOV
353
        if (grid.primaryKey) {
×
UNCOV
354
            index = data.map((record) => record[grid.primaryKey]).indexOf(rowId);
×
355
        } else {
UNCOV
356
            index = data.indexOf(rowId);
×
357
        }
UNCOV
358
        const state: State = grid.transactions.getState(rowId);
×
UNCOV
359
        const hasRowInNonDeletedState = state && state.type !== TransactionType.DELETE;
×
360

361
        //  if there is a row (index !== -1) and the we have cell in edit mode on same row exit edit mode
362
        //  if there is no row (index === -1), but there is a row in ADD or UPDATE state do as above
363
        //  Otherwise just exit - there is nothing to delete
UNCOV
364
        if (index !== -1 || hasRowInNonDeletedState) {
×
365
            // Always exit edit when row is deleted
UNCOV
366
            this.crudService.endEdit(true);
×
367
        } else {
UNCOV
368
            return;
×
369
        }
370

UNCOV
371
        const record = data[index];
×
UNCOV
372
        const key = record ? record[grid.primaryKey] : undefined;
×
UNCOV
373
        grid.rowDeletedNotifier.next({ data: record, rowData: record, owner: grid, primaryKey: key, rowKey: key });
×
374

UNCOV
375
        this.deleteRowFromData(rowId, index);
×
376

UNCOV
377
        if (grid.selectionService.isRowSelected(rowId)) {
×
UNCOV
378
            grid.selectionService.deselectRowsWithNoEvent([rowId]);
×
379
        } else {
UNCOV
380
            grid.selectionService.clearHeaderCBState();
×
381
        }
UNCOV
382
        grid.pipeTrigger++;
×
UNCOV
383
        grid.notifyChanges();
×
384
        // Data needs to be recalculated if transactions are in place
385
        // If no transactions, `data` will be a reference to the grid getter, otherwise it will be stale
UNCOV
386
        const dataAfterDelete = grid.transactions.enabled ? grid.dataWithAddedInTransactionRows : data;
×
UNCOV
387
        grid.refreshSearch();
×
UNCOV
388
        if (dataAfterDelete.length % grid.perPage === 0 && dataAfterDelete.length / grid.perPage - 1 < grid.page && grid.page !== 0) {
×
UNCOV
389
            grid.page--;
×
390
        }
391

UNCOV
392
        return record;
×
393
    }
394

395
    public get_row_id(rowData) {
UNCOV
396
        return this.grid.primaryKey ? rowData[this.grid.primaryKey] : rowData;
×
397
    }
398

399
    public row_deleted_transaction(rowID: any): boolean {
UNCOV
400
        const grid = this.grid;
×
UNCOV
401
        if (!grid) {
×
402
            return false;
×
403
        }
UNCOV
404
        if (!grid.transactions.enabled) {
×
UNCOV
405
            return false;
×
406
        }
UNCOV
407
        const state = grid.transactions.getState(rowID);
×
UNCOV
408
        if (state) {
×
UNCOV
409
            return state.type === TransactionType.DELETE;
×
410
        }
411

UNCOV
412
        return false;
×
413
    }
414

415
    public get_row_expansion_state(record: any): boolean {
UNCOV
416
        const grid = this.grid;
×
UNCOV
417
        const states = grid.expansionStates;
×
UNCOV
418
        const rowID = grid.primaryKey ? record[grid.primaryKey] : record;
×
UNCOV
419
        const expanded = states.get(rowID);
×
420

UNCOV
421
        if (expanded !== undefined) {
×
UNCOV
422
            return expanded;
×
423
        } else {
UNCOV
424
            return grid.getDefaultExpandState(record);
×
425
        }
426
    }
427

428
    public set_row_expansion_state(rowID: any, expanded: boolean, event?: Event) {
UNCOV
429
        const grid = this.grid;
×
UNCOV
430
        const expandedStates = grid.expansionStates;
×
431

UNCOV
432
        if (!this.allow_expansion_state_change(rowID, expanded)) {
×
UNCOV
433
            return;
×
434
        }
435

UNCOV
436
        const args: IRowToggleEventArgs = {
×
437
            rowKey: rowID,
438
            rowID,
439
            expanded,
440
            event,
441
            cancel: false
442
        };
443

UNCOV
444
        grid.rowToggle.emit(args);
×
445

UNCOV
446
        if (args.cancel) {
×
UNCOV
447
            return;
×
448
        }
UNCOV
449
        expandedStates.set(rowID, expanded);
×
UNCOV
450
        grid.expansionStates = expandedStates;
×
451
        // K.D. 28 Feb, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
452
        // this.crudService.endEdit(false);
453
    }
454

455
    public get_rec_by_id(rowID) {
UNCOV
456
        return this.grid.primaryKey ? this.getRowData(rowID) : rowID;
×
457
    }
458

459
    /**
460
     * Returns the index of the record in the data view by pk or -1 if not found or primaryKey is not set.
461
     *
462
     * @param pk
463
     * @param dataCollection
464
     */
465
    public get_rec_index_by_id(pk: string | number, dataCollection?: any[]): number {
466
        dataCollection = dataCollection || this.grid.data;
×
467
        return this.grid.primaryKey ? dataCollection.findIndex(rec => rec[this.grid.primaryKey] === pk) : -1;
×
468
    }
469

470
    public allow_expansion_state_change(rowID, expanded) {
UNCOV
471
        return this.grid.expansionStates.get(rowID) !== expanded;
×
472
    }
473

474
    public prepare_sorting_expression(stateCollections: Array<Array<any>>, expression: ISortingExpression) {
UNCOV
475
        if (expression.dir === SortingDirection.None) {
×
UNCOV
476
            stateCollections.forEach(state => {
×
UNCOV
477
                state.splice(state.findIndex((expr) => expr.fieldName === expression.fieldName), 1);
×
478
            });
UNCOV
479
            return;
×
480
        }
481

482
        /**
483
         * We need to make sure the states in each collection with same fields point to the same object reference.
484
         * If the different state collections provided have different sizes we need to get the largest one.
485
         * That way we can get the state reference from the largest one that has the same fieldName as the expression to prepare.
486
         */
UNCOV
487
        let maxCollection = stateCollections[0];
×
UNCOV
488
        for (let i = 1; i < stateCollections.length; i++) {
×
489
            if (maxCollection.length < stateCollections[i].length) {
×
490
                maxCollection = stateCollections[i];
×
491
            }
492
        }
UNCOV
493
        const maxExpr = maxCollection.find((expr) => expr.fieldName === expression.fieldName);
×
494

UNCOV
495
        stateCollections.forEach(collection => {
×
UNCOV
496
            const myExpr = collection.find((expr) => expr.fieldName === expression.fieldName);
×
UNCOV
497
            if (!myExpr && !maxExpr) {
×
498
                // Expression with this fieldName is missing from the current and the max collection.
UNCOV
499
                collection.push(expression);
×
UNCOV
500
            } else if (!myExpr && maxExpr) {
×
501
                // Expression with this fieldName is missing from the current and but the max collection has.
502
                collection.push(maxExpr);
×
503
                Object.assign(maxExpr, expression);
×
504
            } else {
505
                // The current collection has the expression so just update it.
UNCOV
506
                Object.assign(myExpr, expression);
×
507
            }
508
        });
509
    }
510

511
    public prepare_grouping_expression(stateCollections: Array<Array<any>>, expression: IGroupingExpression) {
UNCOV
512
        if (expression.dir === SortingDirection.None) {
×
513
            stateCollections.forEach(state => {
×
514
                state.splice(state.findIndex((expr) => expr.fieldName === expression.fieldName), 1);
×
515
            });
516
            return;
×
517
        }
518

519
        /**
520
         * We need to make sure the states in each collection with same fields point to the same object reference.
521
         * If the different state collections provided have different sizes we need to get the largest one.
522
         * That way we can get the state reference from the largest one that has the same fieldName as the expression to prepare.
523
         */
UNCOV
524
        let maxCollection = stateCollections[0];
×
UNCOV
525
        for (let i = 1; i < stateCollections.length; i++) {
×
526
            if (maxCollection.length < stateCollections[i].length) {
×
527
                maxCollection = stateCollections[i];
×
528
            }
529
        }
UNCOV
530
        const maxExpr = maxCollection.find((expr) => expr.fieldName === expression.fieldName);
×
531

UNCOV
532
        stateCollections.forEach(collection => {
×
UNCOV
533
            const myExpr = collection.find((expr) => expr.fieldName === expression.fieldName);
×
UNCOV
534
            if (!myExpr && !maxExpr) {
×
535
                // Expression with this fieldName is missing from the current and the max collection.
UNCOV
536
                collection.push(expression);
×
UNCOV
537
            } else if (!myExpr && maxExpr) {
×
538
                // Expression with this fieldName is missing from the current and but the max collection has.
539
                collection.push(maxExpr);
×
540
                Object.assign(maxExpr, expression);
×
541
            } else {
542
                // The current collection has the expression so just update it.
UNCOV
543
                Object.assign(myExpr, expression);
×
544
            }
545
        });
546
    }
547

548
    public remove_grouping_expression(_fieldName) {
549
    }
550

551
    public filterDataByExpressions(expressionsTree: IFilteringExpressionsTree): any[] {
UNCOV
552
        let data = this.get_all_data();
×
553

UNCOV
554
        if (expressionsTree.filteringOperands.length) {
×
UNCOV
555
            const state = { expressionsTree, strategy: this.grid.filterStrategy };
×
UNCOV
556
            data = FilterUtil.filter(cloneArray(data), state, this.grid);
×
557
        }
558

UNCOV
559
        return data;
×
560
    }
561

562
    public sortDataByExpressions(data: any[], expressions: ISortingExpression[]) {
563
        return DataUtil.sort(cloneArray(data), expressions, this.grid.sortStrategy, this.grid);
×
564
    }
565

566
    public pin_row(rowID: any, index?: number, row?: RowType): void {
UNCOV
567
        const grid = (this.grid as any);
×
UNCOV
568
        if (grid._pinnedRecordIDs.indexOf(rowID) !== -1) {
×
569
            return;
×
570
        }
UNCOV
571
        const eventArgs = this.get_pin_row_event_args(rowID, index, row, true);
×
UNCOV
572
        grid.rowPinning.emit(eventArgs);
×
573

UNCOV
574
        if (eventArgs.cancel) {
×
575
            return;
×
576
        }
UNCOV
577
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : grid._pinnedRecordIDs.length;
×
UNCOV
578
        grid._pinnedRecordIDs.splice(insertIndex, 0, rowID);
×
579
    }
580

581
    public unpin_row(rowID: any, row: RowType): void {
UNCOV
582
        const grid = (this.grid as any);
×
UNCOV
583
        const index = grid._pinnedRecordIDs.indexOf(rowID);
×
UNCOV
584
        if (index === -1) {
×
585
            return;
×
586
        }
UNCOV
587
        const eventArgs = this.get_pin_row_event_args(rowID, null , row, false);
×
UNCOV
588
        grid.rowPinning.emit(eventArgs);
×
589

UNCOV
590
        if (eventArgs.cancel) {
×
591
            return;
×
592
        }
UNCOV
593
        grid._pinnedRecordIDs.splice(index, 1);
×
594
    }
595

596
    public get_pin_row_event_args(rowID: any, index?: number, row?: RowType, pinned?: boolean) {
UNCOV
597
        const eventArgs: IPinRowEventArgs = {
×
598
            isPinned: pinned ? true : false,
×
599
            rowKey: rowID,
600
            rowID,
601
            row,
602
            cancel: false
603
        }
UNCOV
604
        if (typeof index === 'number') {
×
UNCOV
605
            eventArgs.insertAtIndex = index <= this.grid.pinnedRecords.length ? index : this.grid.pinnedRecords.length;
×
606
        }
UNCOV
607
        return eventArgs;
×
608
    }
609

610
    /**
611
     * Updates related row of provided grid's data source with provided new row value
612
     *
613
     * @param grid Grid to update data for
614
     * @param rowID ID of the row to update
615
     * @param rowValueInDataSource Initial value of the row as it is in data source
616
     * @param rowCurrentValue Current value of the row as it is with applied previous transactions
617
     * @param rowNewValue New value of the row
618
     */
619
    protected updateData(grid, rowID, rowValueInDataSource: any, rowCurrentValue: any, rowNewValue: { [x: string]: any }) {
UNCOV
620
        if (grid.transactions.enabled) {
×
UNCOV
621
            const transaction: Transaction = {
×
622
                id: rowID,
623
                type: TransactionType.UPDATE,
624
                newValue: rowNewValue
625
            };
UNCOV
626
            grid.transactions.add(transaction, rowCurrentValue);
×
627
        } else {
UNCOV
628
            mergeObjects(rowValueInDataSource, rowNewValue);
×
629
        }
630
    }
631

632

633
    protected update_row_in_array(value: any, rowID: any, index: number) {
634
        const grid = this.grid;
×
635
        grid.data[index] = value;
×
636
    }
637

638
    protected getSortStrategyPerColumn(fieldName: string) {
639
        return this.get_column_by_name(fieldName) ?
×
640
            this.get_column_by_name(fieldName).sortStrategy : undefined;
641
    }
642

643
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc