• 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

47.83
/projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts
1
import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface';
2
import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree';
3
import { resolveNestedPath, parseDate, formatDate, formatCurrency } from '../core/utils';
4
import { ColumnType, GridType } from '../grids/common/grid.interface';
5
import { GridColumnDataType } from './data-util';
6
import { SortingDirection } from './sorting-strategy';
7
import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common';
8
import { IFilteringState } from './filtering-state.interface';
9
import { isTree } from './expressions-tree-util';
10

11
const DateType = 'date';
2✔
12
const DateTimeType = 'dateTime';
2✔
13
const TimeType = 'time';
2✔
14

15
export class FilterUtil {
16
    public static filter<T>(data: T[], state: IFilteringState, grid?: GridType): T[] {
17
        if (!state.strategy) {
23!
UNCOV
18
            state.strategy = new FilteringStrategy();
×
19
        }
20
        return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid);
23✔
21
    }
22
}
23

24
export interface IFilteringStrategy {
25
    filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree,
26
        grid?: GridType): any[];
27
    /* csSuppress */
28
    getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree) : Promise<IgxFilterItem[]>;
29
}
30

31
/* csSuppress */
32
export interface IgxFilterItem {
33
    value: any;
34
    label?: string;
35
    children?: IgxFilterItem[];
36
}
37

38
/* csSuppress */
39
export abstract class BaseFilteringStrategy implements IFilteringStrategy  {
40
    // protected
41
    public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean {
42
        const cond = expr.condition;
835✔
43
        const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid);
835✔
44
        return cond.logic(val, expr.searchVal, expr.ignoreCase);
835✔
45
    }
46

47
    // protected
48
    public matchRecord(rec: any, expressions: IFilteringExpressionsTree | IFilteringExpression, grid?: GridType): boolean {
49
        if (expressions) {
1,265✔
50
            if (isTree(expressions)) {
1,265✔
51
                const expressionsTree = expressions;
430✔
52
                const operator = expressionsTree.operator as FilteringLogic;
430✔
53
                let matchOperand;
54

55
                if (expressionsTree.filteringOperands && expressionsTree.filteringOperands.length) {
430✔
56
                    for (const operand of expressionsTree.filteringOperands) {
227✔
57
                        matchOperand = this.matchRecord(rec, operand, grid);
859✔
58

59
                        // Return false if at least one operand does not match and the filtering logic is And
60
                        if (!matchOperand && operator === FilteringLogic.And) {
859✔
61
                            return false;
142✔
62
                        }
63

64
                        // Return true if at least one operand matches and the filtering logic is Or
65
                        if (matchOperand && operator === FilteringLogic.Or) {
717✔
66
                            return true;
19✔
67
                        }
68
                    }
69

70
                    return matchOperand;
66✔
71
                }
72

73
                return true;
203✔
74
            } else {
75
                const expression = expressions;
835✔
76
                const column = grid && grid.getColumnByName(expression.fieldName);
835✔
77
                const isDate = column ? column.dataType === DateType || column.dataType === DateTimeType : false;
835✔
78
                const isTime = column ? column.dataType === TimeType : false;
835✔
79
                return this.findMatchByExpression(rec, expression, isDate, isTime, grid);
835✔
80
            }
81
        }
82

UNCOV
83
        return true;
×
84
    }
85

86
    public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise<IgxFilterItem[]> {
87

UNCOV
88
        let data = column.grid.gridAPI.filterDataByExpressions(tree);
×
UNCOV
89
        data = column.grid.gridAPI.sortDataByExpressions(data,
×
90
            [{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }]);
91

UNCOV
92
        const columnField = column.field;
×
UNCOV
93
        let filterItems: IgxFilterItem[] = data.map(record => {
×
UNCOV
94
            let value = resolveNestedPath(record, columnField);
×
UNCOV
95
            const applyFormatter = column.formatter && this.shouldFormatFilterValues(column);
×
96

UNCOV
97
            value = applyFormatter ?
×
98
                column.formatter(value, record) :
99
                value;
100

UNCOV
101
            return {
×
102
                value,
103
                label: this.getFilterItemLabel(column, value, !applyFormatter, record)
104
            };
105
        });
UNCOV
106
        filterItems = this.getUniqueFilterItems(column, filterItems);
×
107

UNCOV
108
        return Promise.resolve(filterItems);
×
109
    }
110

111
    protected getFilterItemLabel(column: ColumnType, value: any, applyFormatter: boolean, data: any) {
UNCOV
112
        if (column.formatter) {
×
UNCOV
113
            if (applyFormatter) {
×
UNCOV
114
                return column.formatter(value, data);
×
115
            }
UNCOV
116
            return value;
×
117
        }
118

UNCOV
119
        const { display, format, digitsInfo, currencyCode, timezone } = column.pipeArgs;
×
UNCOV
120
        const locale = column.grid.locale;
×
121

UNCOV
122
        switch (column.dataType) {
×
123
            case GridColumnDataType.Date:
124
            case GridColumnDataType.DateTime:
125
            case GridColumnDataType.Time:
UNCOV
126
                return formatDate(value, format, locale, timezone);
×
127
            case GridColumnDataType.Currency:
UNCOV
128
                return formatCurrency(value, currencyCode || getLocaleCurrencyCode(locale), display, digitsInfo, locale);
×
129
            case GridColumnDataType.Number:
UNCOV
130
                return formatNumber(value, locale, digitsInfo);
×
131
            case GridColumnDataType.Percent:
UNCOV
132
                return formatPercent(value, locale, digitsInfo);
×
133
            default:
UNCOV
134
                return value;
×
135
        }
136
    }
137

138
    protected getUniqueFilterItems(column: ColumnType, filterItems: IgxFilterItem[]) {
UNCOV
139
        const filteredUniqueValues = filterItems.reduce((map, item) => {
×
UNCOV
140
            let key = item.value;
×
141

UNCOV
142
            if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) {
×
UNCOV
143
                key = key?.toString().toLowerCase();
×
UNCOV
144
            } else if (column.dataType === GridColumnDataType.DateTime) {
×
UNCOV
145
                key = item.value?.toString();
×
UNCOV
146
                item.value = key ? new Date(key) : key;
×
UNCOV
147
            } else if (column.dataType === GridColumnDataType.Time) {
×
UNCOV
148
                const date = key ? new Date(key) : key;
×
UNCOV
149
                key = date ? new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds()) : key;
×
UNCOV
150
                item.value = key ? new Date(key) : key;
×
UNCOV
151
            } else if (column.dataType === GridColumnDataType.Date) {
×
UNCOV
152
                const date = key ? new Date(key) : key;
×
UNCOV
153
                key = date ? new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString() : key;
×
UNCOV
154
                item.value = date;
×
155
            }
156

UNCOV
157
            return map.has(key) ? map : map.set(key, item)
×
158
        }, new Map());
UNCOV
159
        const uniqueValues = Array.from(filteredUniqueValues.values());
×
160

UNCOV
161
        return uniqueValues;
×
162
    }
163

164
    protected shouldFormatFilterValues(_column: ColumnType): boolean {
UNCOV
165
        return false;
×
166
    }
167

168
    public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree,
169
        advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[];
170

171
    protected abstract getFieldValue(rec: any, fieldName: string, isDate?: boolean, isTime?: boolean, grid?: GridType): any;
172
}
173

174
/* csSuppress */
175
export class NoopFilteringStrategy extends BaseFilteringStrategy {
176
    protected getFieldValue(rec: any, _fieldName: string) {
177
        return rec;
×
178
    }
179
    private static _instance: NoopFilteringStrategy = null;
2✔
180

181
    public static instance() {
UNCOV
182
        return this._instance || (this._instance = new NoopFilteringStrategy());
×
183
    }
184

185
    public filter(data: any[], _: IFilteringExpressionsTree, __?: IFilteringExpressionsTree): any[] {
UNCOV
186
        return data;
×
187
    }
188
}
189

190

191
export class FilteringStrategy extends BaseFilteringStrategy {
192
    private static _instance: FilteringStrategy = null;
2✔
193

194
    constructor() {
195
        super();
41✔
196
    }
197

198
    public static instance() {
199
        return this._instance || (this._instance = new this());
×
200
    }
201

202
    public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree,
203
        grid: GridType): T[] {
204
        let i;
205
        let rec;
206
        const len = data.length;
23✔
207
        const res: T[] = [];
23✔
208

209
        if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) {
23!
UNCOV
210
            return data;
×
211
        }
212
        for (i = 0; i < len; i++) {
23✔
213
            rec = data[i];
203✔
214
            if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
203✔
215
                res.push(rec);
43✔
216
            }
217
        }
218
        return res;
23✔
219
    }
220

221
    protected getFieldValue(rec: any, fieldName: string, isDate = false, isTime = false, grid?: GridType): any {
×
222
        const column = grid?.getColumnByName(fieldName);
835✔
223
        let value = resolveNestedPath(rec, fieldName);
835✔
224

225
        value = column?.formatter && this.shouldFormatFilterValues(column) ?
835✔
226
            column.formatter(value, rec) :
227
            value && (isDate || isTime) ? parseDate(value) : value;
2,910!
228

229
        return value;
835✔
230
    }
231
}
232
export class FormattedValuesFilteringStrategy extends FilteringStrategy {
233
    /**
234
     * Creates a new instance of FormattedValuesFilteringStrategy.
235
     *
236
     * @param fields An array of column field names that should be formatted.
237
     * If omitted the values of all columns which has formatter will be formatted.
238
     */
239
    constructor(private fields?: string[]) {
2✔
240
        super();
2✔
241
    }
242

243
    protected override shouldFormatFilterValues(column: ColumnType): boolean {
244
        return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field);
16✔
245
    }
246
}
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