• 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

84.29
/projects/igniteui-angular/src/lib/data-operations/merge-strategy.ts
1
import { columnFieldPath, parseDate, resolveNestedPath } from '../core/utils';
2
import { GridType } from '../grids/common/grid.interface';
3

4

5

6

7
export interface IMergeByResult {
8
    rowSpan: number;
9
    root?: any;
10
}
11

12
/**
13
 * Merge strategy interface.
14
 */
15
export interface IGridMergeStrategy {
16
    /* blazorSuppress */
17
    /**
18
     * Function that processes merging of the whole data per merged field.
19
     * Returns collection where object has reference to the original record and map of the cell merge metadata per field.
20
     */
21
    merge: (
22
        /* The original data to merge. */
23
        data: any[],
24
        /* The field in the data to merge. */
25
        field: string,
26
        /* Custom comparer function to use for field. */
27
        comparer: (prevRecord: any, currentRecord: any, field: string) => boolean,
28
        /* Existing merge result to which to add the field specific metadata for merging. */
29
        result: any[],
30
        /* The active row indexes, where merging should break the sequence. */
31
        activeRowIndexes: number[],
32
        /* (Optional) Indicates if the field is of type Date. */
33
        isDate?: boolean,
34
        /* (Optional) Indicates if the field is of type Time. */
35
        isTime?: boolean,
36
        /* (Optional) Reference to the grid */
37
        grid?: GridType
38
    ) => any[];
39
    /**
40
     * Function that compares values for merging. Returns true if same, false if different.
41
     */
42
    comparer: (prevRecord: any, record: any, field: string) => boolean;
43
}
44

45
export class DefaultMergeStrategy implements IGridMergeStrategy {
46
    protected static _instance: DefaultMergeStrategy = null;
3✔
47

48
    public static instance(): DefaultMergeStrategy {
NEW
49
        return this._instance || (this._instance = new this());
×
50
    }
51

52
    /* blazorSuppress */
53
    public merge(
54
        data: any[],
55
        field: string,
56
        comparer: (prevRecord: any, record: any, field: string, isDate?: boolean, isTime?: boolean) => boolean = this.comparer,
85✔
57
        result: any[],
58
        activeRowIndexes: number[],
59
        isDate = false,
×
60
        isTime = false,
×
61
        grid?: GridType
62
    ) {
63
        let prev = null;
86✔
64
        let index = 0;
86✔
65
        for (const rec of data) {
86✔
66

67
            const recData = result[index];
727✔
68
            // if this is active row or some special record type - add and skip merging
69
            if (activeRowIndexes.indexOf(index) != -1 || (grid && grid.isDetailRecord(rec) || grid.isGroupByRecord(rec) || grid.isChildGridRecord(rec))) {
727✔
70
                if (!recData) {
33✔
71
                    result.push(rec);
33✔
72
                }
73
                prev = null;
33✔
74
                index++;
33✔
75
                continue;
33✔
76
            }
77
            let recToUpdateData = recData ?? { recordRef: grid.isGhostRecord(rec) ? rec.recordRef : rec, cellMergeMeta: new Map<string, IMergeByResult>(), ghostRecord: rec.ghostRecord };
694✔
78
            recToUpdateData.cellMergeMeta.set(field, { rowSpan: 1 });
694✔
79
            if (prev && comparer.call(this, prev.recordRef, recToUpdateData.recordRef, field, isDate, isTime) && prev.ghostRecord === recToUpdateData.ghostRecord) {
694✔
80
                const root = prev.cellMergeMeta.get(field)?.root ?? prev;
219✔
81
                root.cellMergeMeta.get(field).rowSpan += 1;
219✔
82
                recToUpdateData.cellMergeMeta.get(field).root = root;
219✔
83
            }
84
            prev = recToUpdateData;
694✔
85
            if (!recData) {
694✔
86
                result.push(recToUpdateData);
676✔
87
            }
88
            index++;
694✔
89
        }
90
        return result;
86✔
91
    }
92

93
    /* blazorSuppress */
94
    public comparer(prevRecord: any, record: any, field: string, isDate = false, isTime = false): boolean {
×
95
        const a = this.getFieldValue(prevRecord,field, isDate, isTime);
516✔
96
        const b = this.getFieldValue(record,field, isDate, isTime);
516✔
97
        const an = (a === null || a === undefined);
516✔
98
        const bn = (b === null || b === undefined);
516✔
99
        if (an) {
516✔
100
            if (bn) {
57✔
101
                return true;
1✔
102
            }
103
            return false;
56✔
104
        } else if (bn) {
459✔
105
            return false;
58✔
106
        }
107
        return a === b;
401✔
108
    }
109

110
    /**
111
     * Retrieves the value of the specified field from the given object, considering date and time data types.
112
     * `key`: The key of the field to retrieve.
113
     * `isDate`: (Optional) Indicates if the field is of type Date.
114
     * `isTime`: (Optional) Indicates if the field is of type Time.
115
     * Returns the value of the specified field in the data object.
116
     * @internal
117
     */
118
    protected getFieldValue<T>(obj: T, key: string, isDate = false, isTime = false) {
×
119
        let resolvedValue = resolveNestedPath(obj, columnFieldPath(key));
1,168✔
120
        if (isDate || isTime) {
1,168✔
121
            resolvedValue = this.getDateValue(resolvedValue, isDate, isTime);
16✔
122
        }
123
        return resolvedValue;
1,168✔
124
    }
125

126
    /**
127
   * @internal
128
   */
129
    protected getDateValue<T>(obj: T, isDate = false, isTime = false) {
×
130
        let date = obj instanceof Date ? obj : parseDate(obj);
16✔
131
        let resolvedValue;
132
        if (isDate && isTime) {
16!
133
            // date + time
NEW
134
            resolvedValue = date.getTime();
×
135
        } else if (date && isDate && !isTime) {
16✔
136
            // date, but no time
137
            resolvedValue = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0).getTime();
13✔
138
        } else if (date && isTime && !isDate) {
3!
139
            // just time
NEW
140
            resolvedValue = new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds());
×
141
        }
142
        return resolvedValue;
16✔
143
    }
144
}
145

146

147
export class DefaultTreeGridMergeStrategy extends DefaultMergeStrategy {
148
    /* blazorSuppress */
149
    public override comparer(prevRecord: any, record: any, field: string, isDate = false, isTime = false): boolean {
×
150
        const a = this.getFieldValue( prevRecord.data, field, isDate, isTime);
34✔
151
        const b = this.getFieldValue(record.data,field, isDate, isTime);
34✔
152
        const an = (a === null || a === undefined);
34✔
153
        const bn = (b === null || b === undefined);
34✔
154
        if (an) {
34!
NEW
155
            if (bn) {
×
NEW
156
                return true;
×
157
            }
NEW
158
            return false;
×
159
        } else if (bn) {
34!
NEW
160
            return false;
×
161
        }
162
        return a === b;
34✔
163
    }
164
}
165

166
export class ByLevelTreeGridMergeStrategy extends DefaultMergeStrategy {
167
    /* blazorSuppress */
168
    public override comparer(prevRecord: any, record: any, field: string, isDate = false, isTime = false): boolean {
×
169
        const a = this.getFieldValue( prevRecord.data, field, isDate, isTime);
34✔
170
        const b = this.getFieldValue(record.data,field, isDate, isTime);
34✔
171
        const levelA = prevRecord.level;
34✔
172
        const levelB = record.level;
34✔
173
        const an = (a === null || a === undefined);
34✔
174
        const bn = (b === null || b === undefined);
34✔
175
        if (an) {
34!
NEW
176
            if (bn) {
×
NEW
177
                return true;
×
178
            }
NEW
179
            return false;
×
180
        } else if (bn) {
34!
NEW
181
            return false;
×
182
        }
183
        return a === b && levelA === levelB;
34✔
184
    }
185
}
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