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

IgniteUI / igniteui-angular / 14109372524

27 Mar 2025 02:39PM UTC coverage: 91.622%. Remained the same
14109372524

push

github

web-flow
Merge pull request #15614 from IgniteUI/ipetrov/dnd-test-break-down-19.1

fix(query-builder): refactor drag and drop test - 19.1.x

13317 of 15578 branches covered (85.49%)

26858 of 29314 relevant lines covered (91.62%)

34134.49 hits per line

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

97.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) {
484✔
18
            state.strategy = new FilteringStrategy();
5✔
19
        }
20
        return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid);
484✔
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 val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid);
7,061✔
43
        if (expr.condition?.logic) {
7,061✔
44
            return expr.condition.logic(val, expr.searchVal, expr.ignoreCase);
7,059✔
45
        }
46
    }
47

48
    // protected
49
    public matchRecord(rec: any, expressions: IFilteringExpressionsTree | IFilteringExpression, grid?: GridType): boolean {
50
        if (expressions) {
20,234✔
51
            if (isTree(expressions)) {
18,818✔
52
                const expressionsTree = expressions;
11,728✔
53
                const operator = expressionsTree.operator as FilteringLogic;
11,728✔
54
                let matchOperand;
55

56
                if (expressionsTree.filteringOperands && expressionsTree.filteringOperands.length) {
11,728✔
57
                    for (const operand of expressionsTree.filteringOperands) {
11,228✔
58
                        matchOperand = this.matchRecord(rec, operand, grid);
12,560✔
59

60
                        // Return false if at least one operand does not match and the filtering logic is And
61
                        if (!matchOperand && operator === FilteringLogic.And) {
12,560✔
62
                            return false;
7,098✔
63
                        }
64

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

71
                    return matchOperand;
3,657✔
72
                }
73

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

84
        return true;
1,416✔
85
    }
86

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

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

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

98
            value = applyFormatter ?
1,833✔
99
                column.formatter(value, record) :
100
                value;
101

102
            return {
1,833✔
103
                value,
104
                label: this.getFilterItemLabel(column, value, !applyFormatter, record)
105
            };
106
        });
107
        filterItems = this.getUniqueFilterItems(column, filterItems);
224✔
108

109
        return Promise.resolve(filterItems);
224✔
110
    }
111

112
    protected getFilterItemLabel(column: ColumnType, value: any, applyFormatter: boolean, data: any) {
113
        if (column.formatter) {
2,410✔
114
            if (applyFormatter) {
94✔
115
                return column.formatter(value, data);
24✔
116
            }
117
            return value;
70✔
118
        }
119

120
        const { display, format, digitsInfo, currencyCode, timezone } = column.pipeArgs;
2,316✔
121
        const locale = column.grid.locale;
2,316✔
122

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

139
    protected getUniqueFilterItems(column: ColumnType, filterItems: IgxFilterItem[]) {
140
        const filteredUniqueValues = filterItems.reduce((map, item) => {
224✔
141
            let key = item.value;
1,833✔
142

143
            if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) {
1,833✔
144
                key = key?.toString().toLowerCase();
915✔
145
            } else if (column.dataType === GridColumnDataType.DateTime) {
918✔
146
                key = item.value?.toString();
54✔
147
                item.value = key ? new Date(key) : key;
54✔
148
            } else if (column.dataType === GridColumnDataType.Time) {
864✔
149
                const date = key ? new Date(key) : key;
39✔
150
                key = date ? new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds()) : key;
39✔
151
                item.value = key ? new Date(key) : key;
39✔
152
            } else if (column.dataType === GridColumnDataType.Date) {
825✔
153
                const date = key ? new Date(key) : key;
208✔
154
                key = date ? new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString() : key;
208✔
155
                item.value = date;
208✔
156
            }
157

158
            return map.has(key) ? map : map.set(key, item)
1,833✔
159
        }, new Map());
160
        const uniqueValues = Array.from(filteredUniqueValues.values());
224✔
161

162
        return uniqueValues;
224✔
163
    }
164

165
    protected shouldFormatFilterValues(_column: ColumnType): boolean {
166
        return false;
43✔
167
    }
168

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

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

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

182
    public static instance() {
183
        return this._instance || (this._instance = new NoopFilteringStrategy());
1✔
184
    }
185

186
    public filter(data: any[], _: IFilteringExpressionsTree, __?: IFilteringExpressionsTree): any[] {
187
        return data;
1✔
188
    }
189
}
190

191

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

195
    constructor() {
196
        super();
4,233✔
197
    }
198

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

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

210
        if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) {
498✔
211
            return data;
14✔
212
        }
213
        for (i = 0; i < len; i++) {
484✔
214
            rec = data[i];
4,733✔
215
            if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
4,733✔
216
                res.push(rec);
1,183✔
217
            }
218
        }
219
        return res;
484✔
220
    }
221

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

226
        value = column?.formatter && this.shouldFormatFilterValues(column) ?
5,736✔
227
            column.formatter(value, rec) :
228
            value && (isDate || isTime) ? parseDate(value) : value;
19,470✔
229

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

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