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

IgniteUI / igniteui-angular / 16193550997

10 Jul 2025 11:12AM UTC coverage: 4.657% (-87.0%) from 91.64%
16193550997

Pull #16028

github

web-flow
Merge f7a9963b8 into 87246e3ce
Pull Request #16028: fix(radio-group): dynamically added radio buttons do not initialize

178 of 15764 branches covered (1.13%)

18 of 19 new or added lines in 2 files covered. (94.74%)

25721 existing lines in 324 files now uncovered.

1377 of 29570 relevant lines covered (4.66%)

0.53 hits per line

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

0.35
/projects/igniteui-angular/src/lib/grids/api.service.ts
1
import { Injectable } from '@angular/core';
2
import { Subject } from 'rxjs';
3
import { cloneArray, reverseMapper, mergeObjects } from '../core/utils';
4
import { DataUtil, GridColumnDataType } from '../data-operations/data-util';
5
import { IFilteringExpressionsTree } from '../data-operations/filtering-expressions-tree';
6
import { Transaction, TransactionType, State } from '../services/transaction/transaction';
7
import { IgxCell, IgxGridCRUDService, IgxEditRow } from './common/crud.service';
8
import { CellType, ColumnType, GridServiceType, GridType, RowType } from './common/grid.interface';
9
import { IGridEditEventArgs, IPinRowEventArgs, IRowToggleEventArgs } from './common/events';
10
import { IgxColumnMovingService } from './moving/moving.service';
11
import { IGroupingExpression } from '../data-operations/grouping-expression.interface';
12
import { ISortingExpression, SortingDirection } from '../data-operations/sorting-strategy';
13
import { FilterUtil } from '../data-operations/filtering-strategy';
14

15
/**
16
 * @hidden
17
 */
18
@Injectable()
19
export class GridBaseAPIService<T extends GridType> implements GridServiceType {
3✔
20

21

22
    public grid: T;
UNCOV
23
    protected destroyMap: Map<string, Subject<boolean>> = new Map<string, Subject<boolean>>();
×
24

25
    constructor(
UNCOV
26
        public crudService: IgxGridCRUDService,
×
UNCOV
27
        public cms: IgxColumnMovingService
×
28
    ) { }
29

30
    public get_column_by_name(name: string): ColumnType {
UNCOV
31
        return this.grid.columns.find((col: ColumnType) => col.field === name);
×
32
    }
33

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

63
    /**
64
     * @hidden
65
     * @internal
66
     */
67
    public getRowData(rowID: any) {
UNCOV
68
        const data = this.get_all_data(this.grid.transactions.enabled);
×
UNCOV
69
        const index = this.get_row_index_in_data(rowID, data);
×
UNCOV
70
        return data[index];
×
71
    }
72

73
    public get_row_index_in_data(rowID: any, dataCollection?: any[]): number {
UNCOV
74
        const grid = this.grid;
×
UNCOV
75
        if (!grid) {
×
76
            return -1;
×
77
        }
UNCOV
78
        const data = dataCollection ?? this.get_all_data(grid.transactions.enabled);
×
UNCOV
79
        return grid.primaryKey ? data.findIndex(record => record.recordRef ? record.recordRef[grid.primaryKey] === rowID
×
80
            : record[grid.primaryKey] === rowID) : data.indexOf(rowID);
81
    }
82

83
    public get_row_by_key(rowSelector: any): RowType {
UNCOV
84
        if (!this.grid) {
×
85
            return null;
×
86
        }
UNCOV
87
        const primaryKey = this.grid.primaryKey;
×
UNCOV
88
        if (primaryKey !== undefined && primaryKey !== null) {
×
UNCOV
89
            return this.grid.dataRowList.find((row) => row.data[primaryKey] === rowSelector);
×
90
        } else {
UNCOV
91
            return this.grid.dataRowList.find((row) => row.data === rowSelector);
×
92
        }
93
    }
94

95
    public get_row_by_index(rowIndex: number): RowType {
UNCOV
96
        return this.grid.rowList.find((row) => row.index === rowIndex);
×
97
    }
98

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

114
    public get_cell_by_key(rowSelector: any, field: string): CellType {
UNCOV
115
        const row = this.get_row_by_key(rowSelector);
×
UNCOV
116
        if (row && row.cells) {
×
UNCOV
117
            return row.cells.find((cell) => cell.column.field === field);
×
118
        }
119
    }
120

121
    public get_cell_by_index(rowIndex: number, columnID: number | string): CellType {
UNCOV
122
        const row = this.get_row_by_index(rowIndex);
×
UNCOV
123
        const hasCells = row && row.cells;
×
UNCOV
124
        if (hasCells && typeof columnID === 'number') {
×
UNCOV
125
            return row.cells.find((cell) => cell.column.index === columnID);
×
126
        }
UNCOV
127
        if (hasCells && typeof columnID === 'string') {
×
UNCOV
128
            return row.cells.find((cell) => cell.column.field === columnID);
×
129
        }
130

131
    }
132

133
    public get_cell_by_visible_index(rowIndex: number, columnIndex: number): CellType {
UNCOV
134
        const row = this.get_row_by_index(rowIndex);
×
UNCOV
135
        if (row && row.cells) {
×
UNCOV
136
            return row.cells.find((cell) => cell.visibleColumnIndex === columnIndex);
×
137
        }
138
    }
139

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

UNCOV
178
        return args;
×
179
    }
180

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

UNCOV
191
        const args = row.createRowEditEventArgs(true, event);
×
192

193
        // If no valid row is found
UNCOV
194
        if (index === -1) {
×
UNCOV
195
            return args;
×
196
        }
197

UNCOV
198
        if (rowInEditMode) {
×
UNCOV
199
            const hasChanges = grid.transactions.getState(args.rowID, true);
×
UNCOV
200
            grid.transactions.endPending(false);
×
UNCOV
201
            if (!hasChanges) {
×
UNCOV
202
                return args;
×
203
            }
204
        }
205

UNCOV
206
        if (!args.newValue) {
×
207
            return args;
×
208
        }
209

UNCOV
210
        if (hasSummarized) {
×
UNCOV
211
            grid.summaryService.removeSummaries(args.rowID);
×
212
        }
213

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

UNCOV
228
        return args;
×
229
    }
230

231
    public sort(expression: ISortingExpression): void {
UNCOV
232
        if (expression.dir === SortingDirection.None) {
×
UNCOV
233
            this.remove_grouping_expression(expression.fieldName);
×
234
        }
UNCOV
235
        const sortingState = cloneArray(this.grid.sortingExpressions);
×
UNCOV
236
        this.prepare_sorting_expression([sortingState], expression);
×
UNCOV
237
        this.grid.sortingExpressions = sortingState;
×
238
    }
239

240
    public sort_decoupled(expression: IGroupingExpression): void {
241
        if (expression.dir === SortingDirection.None) {
×
242
            this.remove_grouping_expression(expression.fieldName);
×
243
        }
244
        const groupingState = cloneArray((this.grid as any).groupingExpressions);
×
245
        this.prepare_grouping_expression([groupingState], expression);
×
246
        (this.grid as any).groupingExpressions = groupingState;
×
247
    }
248

249
    public sort_multiple(expressions: ISortingExpression[]): void {
UNCOV
250
        const sortingState = cloneArray(this.grid.sortingExpressions);
×
251

UNCOV
252
        for (const each of expressions) {
×
UNCOV
253
            if (each.dir === SortingDirection.None) {
×
254
                this.remove_grouping_expression(each.fieldName);
×
255
            }
UNCOV
256
            this.prepare_sorting_expression([sortingState], each);
×
257
        }
258

UNCOV
259
        this.grid.sortingExpressions = sortingState;
×
260
    }
261

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

UNCOV
265
        for (const each of expressions) {
×
UNCOV
266
            if (each.dir === SortingDirection.None) {
×
267
                this.remove_grouping_expression(each.fieldName);
×
268
            }
UNCOV
269
            this.prepare_grouping_expression([groupingState], each);
×
270
        }
271
    }
272

273
    public clear_sort(fieldName: string) {
UNCOV
274
        const sortingState = this.grid.sortingExpressions;
×
UNCOV
275
        const index = sortingState.findIndex((expr) => expr.fieldName === fieldName);
×
UNCOV
276
        if (index > -1) {
×
UNCOV
277
            sortingState.splice(index, 1);
×
UNCOV
278
            this.grid.sortingExpressions = sortingState;
×
279
        }
280
    }
281

282
    public clear_groupby(_name?: string | Array<string>) {
283
    }
284

285
    public should_apply_number_style(column: ColumnType): boolean {
286
        return column.dataType === GridColumnDataType.Number;
×
287
    }
288

289
    public get_data(): any[] {
UNCOV
290
        const grid = this.grid;
×
UNCOV
291
        const data = grid.data ? grid.data : [];
×
UNCOV
292
        return data;
×
293
    }
294

295
    public get_all_data(includeTransactions = false): any[] {
×
UNCOV
296
        const grid = this.grid;
×
UNCOV
297
        let data = grid && grid.data ? grid.data : [];
×
UNCOV
298
        data = includeTransactions ? grid.dataWithAddedInTransactionRows : data;
×
UNCOV
299
        return data;
×
300
    }
301

302
    public get_filtered_data(): any[] {
303
        return this.grid.filteredData;
×
304
    }
305

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

321
    public deleteRowFromData(rowID: any, index: number) {
322
        //  if there is a row (index !== 0) delete it
323
        //  if there is a row in ADD or UPDATE state change it's state to DELETE
UNCOV
324
        const grid = this.grid;
×
UNCOV
325
        if (index !== -1) {
×
UNCOV
326
            if (grid.transactions.enabled) {
×
UNCOV
327
                const transaction: Transaction = { id: rowID, type: TransactionType.DELETE, newValue: null };
×
UNCOV
328
                grid.transactions.add(transaction, grid.data[index]);
×
329
            } else {
UNCOV
330
                grid.data.splice(index, 1);
×
331
            }
332
        } else {
UNCOV
333
            const state: State = grid.transactions.getState(rowID);
×
UNCOV
334
            grid.transactions.add({ id: rowID, type: TransactionType.DELETE, newValue: null }, state && state.recordRef);
×
335
        }
UNCOV
336
        grid.validation.clear(rowID);
×
337
    }
338

339
    public deleteRowById(rowId: any): any {
340
        let index: number;
UNCOV
341
        const grid = this.grid;
×
UNCOV
342
        const data = this.get_all_data(grid.transactions.enabled);
×
UNCOV
343
        if (grid.primaryKey) {
×
UNCOV
344
            index = data.map((record) => record[grid.primaryKey]).indexOf(rowId);
×
345
        } else {
UNCOV
346
            index = data.indexOf(rowId);
×
347
        }
UNCOV
348
        const state: State = grid.transactions.getState(rowId);
×
UNCOV
349
        const hasRowInNonDeletedState = state && state.type !== TransactionType.DELETE;
×
350

351
        //  if there is a row (index !== -1) and the we have cell in edit mode on same row exit edit mode
352
        //  if there is no row (index === -1), but there is a row in ADD or UPDATE state do as above
353
        //  Otherwise just exit - there is nothing to delete
UNCOV
354
        if (index !== -1 || hasRowInNonDeletedState) {
×
355
            // Always exit edit when row is deleted
UNCOV
356
            this.crudService.endEdit(true);
×
357
        } else {
UNCOV
358
            return;
×
359
        }
360

UNCOV
361
        const record = data[index];
×
UNCOV
362
        const key = record ? record[grid.primaryKey] : undefined;
×
UNCOV
363
        grid.rowDeletedNotifier.next({ data: record, rowData: record, owner: grid, primaryKey: key, rowKey: key });
×
364

UNCOV
365
        this.deleteRowFromData(rowId, index);
×
366

UNCOV
367
        if (grid.selectionService.isRowSelected(rowId)) {
×
UNCOV
368
            grid.selectionService.deselectRowsWithNoEvent([rowId]);
×
369
        } else {
UNCOV
370
            grid.selectionService.clearHeaderCBState();
×
371
        }
UNCOV
372
        grid.pipeTrigger++;
×
UNCOV
373
        grid.notifyChanges();
×
374
        // Data needs to be recalculated if transactions are in place
375
        // If no transactions, `data` will be a reference to the grid getter, otherwise it will be stale
UNCOV
376
        const dataAfterDelete = grid.transactions.enabled ? grid.dataWithAddedInTransactionRows : data;
×
UNCOV
377
        grid.refreshSearch();
×
UNCOV
378
        if (dataAfterDelete.length % grid.perPage === 0 && dataAfterDelete.length / grid.perPage - 1 < grid.page && grid.page !== 0) {
×
UNCOV
379
            grid.page--;
×
380
        }
381

UNCOV
382
        return record;
×
383
    }
384

385
    public get_row_id(rowData) {
UNCOV
386
        return this.grid.primaryKey ? rowData[this.grid.primaryKey] : rowData;
×
387
    }
388

389
    public row_deleted_transaction(rowID: any): boolean {
UNCOV
390
        const grid = this.grid;
×
UNCOV
391
        if (!grid) {
×
392
            return false;
×
393
        }
UNCOV
394
        if (!grid.transactions.enabled) {
×
UNCOV
395
            return false;
×
396
        }
UNCOV
397
        const state = grid.transactions.getState(rowID);
×
UNCOV
398
        if (state) {
×
UNCOV
399
            return state.type === TransactionType.DELETE;
×
400
        }
401

UNCOV
402
        return false;
×
403
    }
404

405
    public get_row_expansion_state(record: any): boolean {
UNCOV
406
        const grid = this.grid;
×
UNCOV
407
        const states = grid.expansionStates;
×
UNCOV
408
        const rowID = grid.primaryKey ? record[grid.primaryKey] : record;
×
UNCOV
409
        const expanded = states.get(rowID);
×
410

UNCOV
411
        if (expanded !== undefined) {
×
UNCOV
412
            return expanded;
×
413
        } else {
UNCOV
414
            return grid.getDefaultExpandState(record);
×
415
        }
416
    }
417

418
    public set_row_expansion_state(rowID: any, expanded: boolean, event?: Event) {
UNCOV
419
        const grid = this.grid;
×
UNCOV
420
        const expandedStates = grid.expansionStates;
×
421

UNCOV
422
        if (!this.allow_expansion_state_change(rowID, expanded)) {
×
UNCOV
423
            return;
×
424
        }
425

UNCOV
426
        const args: IRowToggleEventArgs = {
×
427
            rowKey: rowID,
428
            rowID,
429
            expanded,
430
            event,
431
            cancel: false
432
        };
433

UNCOV
434
        grid.rowToggle.emit(args);
×
435

UNCOV
436
        if (args.cancel) {
×
UNCOV
437
            return;
×
438
        }
UNCOV
439
        expandedStates.set(rowID, expanded);
×
UNCOV
440
        grid.expansionStates = expandedStates;
×
441
        // K.D. 28 Feb, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
442
        // this.crudService.endEdit(false);
443
    }
444

445
    public get_rec_by_id(rowID) {
UNCOV
446
        return this.grid.primaryKey ? this.getRowData(rowID) : rowID;
×
447
    }
448

449
    /**
450
     * Returns the index of the record in the data view by pk or -1 if not found or primaryKey is not set.
451
     *
452
     * @param pk
453
     * @param dataCollection
454
     */
455
    public get_rec_index_by_id(pk: string | number, dataCollection?: any[]): number {
456
        dataCollection = dataCollection || this.grid.data;
×
457
        return this.grid.primaryKey ? dataCollection.findIndex(rec => rec[this.grid.primaryKey] === pk) : -1;
×
458
    }
459

460
    public allow_expansion_state_change(rowID, expanded) {
UNCOV
461
        return this.grid.expansionStates.get(rowID) !== expanded;
×
462
    }
463

464
    public prepare_sorting_expression(stateCollections: Array<Array<any>>, expression: ISortingExpression) {
UNCOV
465
        if (expression.dir === SortingDirection.None) {
×
UNCOV
466
            stateCollections.forEach(state => {
×
UNCOV
467
                state.splice(state.findIndex((expr) => expr.fieldName === expression.fieldName), 1);
×
468
            });
UNCOV
469
            return;
×
470
        }
471

472
        /**
473
         * We need to make sure the states in each collection with same fields point to the same object reference.
474
         * If the different state collections provided have different sizes we need to get the largest one.
475
         * That way we can get the state reference from the largest one that has the same fieldName as the expression to prepare.
476
         */
UNCOV
477
        let maxCollection = stateCollections[0];
×
UNCOV
478
        for (let i = 1; i < stateCollections.length; i++) {
×
479
            if (maxCollection.length < stateCollections[i].length) {
×
480
                maxCollection = stateCollections[i];
×
481
            }
482
        }
UNCOV
483
        const maxExpr = maxCollection.find((expr) => expr.fieldName === expression.fieldName);
×
484

UNCOV
485
        stateCollections.forEach(collection => {
×
UNCOV
486
            const myExpr = collection.find((expr) => expr.fieldName === expression.fieldName);
×
UNCOV
487
            if (!myExpr && !maxExpr) {
×
488
                // Expression with this fieldName is missing from the current and the max collection.
UNCOV
489
                collection.push(expression);
×
UNCOV
490
            } else if (!myExpr && maxExpr) {
×
491
                // Expression with this fieldName is missing from the current and but the max collection has.
492
                collection.push(maxExpr);
×
493
                Object.assign(maxExpr, expression);
×
494
            } else {
495
                // The current collection has the expression so just update it.
UNCOV
496
                Object.assign(myExpr, expression);
×
497
            }
498
        });
499
    }
500

501
    public prepare_grouping_expression(stateCollections: Array<Array<any>>, expression: IGroupingExpression) {
UNCOV
502
        if (expression.dir === SortingDirection.None) {
×
503
            stateCollections.forEach(state => {
×
504
                state.splice(state.findIndex((expr) => expr.fieldName === expression.fieldName), 1);
×
505
            });
506
            return;
×
507
        }
508

509
        /**
510
         * We need to make sure the states in each collection with same fields point to the same object reference.
511
         * If the different state collections provided have different sizes we need to get the largest one.
512
         * That way we can get the state reference from the largest one that has the same fieldName as the expression to prepare.
513
         */
UNCOV
514
        let maxCollection = stateCollections[0];
×
UNCOV
515
        for (let i = 1; i < stateCollections.length; i++) {
×
516
            if (maxCollection.length < stateCollections[i].length) {
×
517
                maxCollection = stateCollections[i];
×
518
            }
519
        }
UNCOV
520
        const maxExpr = maxCollection.find((expr) => expr.fieldName === expression.fieldName);
×
521

UNCOV
522
        stateCollections.forEach(collection => {
×
UNCOV
523
            const myExpr = collection.find((expr) => expr.fieldName === expression.fieldName);
×
UNCOV
524
            if (!myExpr && !maxExpr) {
×
525
                // Expression with this fieldName is missing from the current and the max collection.
UNCOV
526
                collection.push(expression);
×
UNCOV
527
            } else if (!myExpr && maxExpr) {
×
528
                // Expression with this fieldName is missing from the current and but the max collection has.
529
                collection.push(maxExpr);
×
530
                Object.assign(maxExpr, expression);
×
531
            } else {
532
                // The current collection has the expression so just update it.
UNCOV
533
                Object.assign(myExpr, expression);
×
534
            }
535
        });
536
    }
537

538
    public remove_grouping_expression(_fieldName) {
539
    }
540

541
    public filterDataByExpressions(expressionsTree: IFilteringExpressionsTree): any[] {
UNCOV
542
        let data = this.get_all_data();
×
543

UNCOV
544
        if (expressionsTree.filteringOperands.length) {
×
UNCOV
545
            const state = { expressionsTree, strategy: this.grid.filterStrategy };
×
UNCOV
546
            data = FilterUtil.filter(cloneArray(data), state, this.grid);
×
547
        }
548

UNCOV
549
        return data;
×
550
    }
551

552
    public sortDataByExpressions(data: any[], expressions: ISortingExpression[]) {
UNCOV
553
        return DataUtil.sort(cloneArray(data), expressions, this.grid.sortStrategy, this.grid);
×
554
    }
555

556
    public pin_row(rowID: any, index?: number, row?: RowType): void {
557
        const grid = (this.grid as any);
×
558
        if (grid._pinnedRecordIDs.indexOf(rowID) !== -1) {
×
559
            return;
×
560
        }
561
        const eventArgs = this.get_pin_row_event_args(rowID, index, row, true);
×
562
        grid.rowPinning.emit(eventArgs);
×
563

564
        if (eventArgs.cancel) {
×
565
            return;
×
566
        }
567
        const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : grid._pinnedRecordIDs.length;
×
568
        grid._pinnedRecordIDs.splice(insertIndex, 0, rowID);
×
569
    }
570

571
    public unpin_row(rowID: any, row: RowType): void {
572
        const grid = (this.grid as any);
×
573
        const index = grid._pinnedRecordIDs.indexOf(rowID);
×
574
        if (index === -1) {
×
575
            return;
×
576
        }
577
        const eventArgs = this.get_pin_row_event_args(rowID, null , row, false);
×
578
        grid.rowPinning.emit(eventArgs);
×
579

580
        if (eventArgs.cancel) {
×
581
            return;
×
582
        }
583
        grid._pinnedRecordIDs.splice(index, 1);
×
584
    }
585

586
    public get_pin_row_event_args(rowID: any, index?: number, row?: RowType, pinned?: boolean) {
UNCOV
587
        const eventArgs: IPinRowEventArgs = {
×
588
            isPinned: pinned ? true : false,
×
589
            rowKey: rowID,
590
            rowID,
591
            row,
592
            cancel: false
593
        }
UNCOV
594
        if (typeof index === 'number') {
×
UNCOV
595
            eventArgs.insertAtIndex = index <= this.grid.pinnedRecords.length ? index : this.grid.pinnedRecords.length;
×
596
        }
UNCOV
597
        return eventArgs;
×
598
    }
599

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

622

623
    protected update_row_in_array(value: any, rowID: any, index: number) {
624
        const grid = this.grid;
×
625
        grid.data[index] = value;
×
626
    }
627

628
    protected getSortStrategyPerColumn(fieldName: string) {
629
        return this.get_column_by_name(fieldName) ?
×
630
            this.get_column_by_name(fieldName).sortStrategy : undefined;
631
    }
632

633
}
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