igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
242 lines (200 loc) • 9.88 kB
text/typescript
import { FilteringLogic, IFilteringExpression } from './filtering-expression.interface';
import { FilteringExpressionsTree, IFilteringExpressionsTree } from './filtering-expressions-tree';
import { resolveNestedPath, parseDate, formatDate, formatCurrency } from '../core/utils';
import { ColumnType, GridType } from '../grids/common/grid.interface';
import { GridColumnDataType } from './data-util';
import { SortingDirection } from './sorting-strategy';
import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common';
import { IFilteringState } from './filtering-state.interface';
const DateType = 'date';
const DateTimeType = 'dateTime';
const TimeType = 'time';
export class FilterUtil {
public static filter<T>(data: T[], state: IFilteringState, grid?: GridType): T[] {
if (!state.strategy) {
state.strategy = new FilteringStrategy();
}
return state.strategy.filter(data, state.expressionsTree, state.advancedExpressionsTree, grid);
}
}
export interface IFilteringStrategy {
filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree,
grid?: GridType): any[];
getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree) : Promise<IgxFilterItem[]>;
}
export interface IgxFilterItem {
value: any;
label?: string;
children?: IgxFilterItem[];
}
export abstract class BaseFilteringStrategy implements IFilteringStrategy {
// protected
public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean {
const cond = expr.condition;
const val = this.getFieldValue(rec, expr.fieldName, isDate, isTime, grid);
return cond.logic(val, expr.searchVal, expr.ignoreCase);
}
// protected
public matchRecord(rec: any, expressions: IFilteringExpressionsTree | IFilteringExpression, grid?: GridType): boolean {
if (expressions) {
if (expressions instanceof FilteringExpressionsTree) {
const expressionsTree = expressions as IFilteringExpressionsTree;
const operator = expressionsTree.operator as FilteringLogic;
let matchOperand;
if (expressionsTree.filteringOperands && expressionsTree.filteringOperands.length) {
for (const operand of expressionsTree.filteringOperands) {
matchOperand = this.matchRecord(rec, operand, grid);
// Return false if at least one operand does not match and the filtering logic is And
if (!matchOperand && operator === FilteringLogic.And) {
return false;
}
// Return true if at least one operand matches and the filtering logic is Or
if (matchOperand && operator === FilteringLogic.Or) {
return true;
}
}
return matchOperand;
}
return true;
} else {
const expression = expressions as IFilteringExpression;
const column = grid && grid.getColumnByName(expression.fieldName);
const isDate = column ? column.dataType === DateType || column.dataType === DateTimeType : false;
const isTime = column ? column.dataType === TimeType : false;
return this.findMatchByExpression(rec, expression, isDate, isTime, grid);
}
}
return true;
}
public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise<IgxFilterItem[]> {
let data = column.grid.gridAPI.filterDataByExpressions(tree);
data = column.grid.gridAPI.sortDataByExpressions(data,
[{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }]);
const columnField = column.field;
let filterItems: IgxFilterItem[] = data.map(record => {
let value = resolveNestedPath(record, columnField);
const applyFormatter = column.formatter && this.shouldFormatFilterValues(column);
value = applyFormatter ?
column.formatter(value, record) :
value;
return {
value,
label: this.getFilterItemLabel(column, value, !applyFormatter, record)
};
});
filterItems = this.getUniqueFilterItems(column, filterItems);
return Promise.resolve(filterItems);
}
protected getFilterItemLabel(column: ColumnType, value: any, applyFormatter: boolean, data: any) {
if (column.formatter) {
if (applyFormatter) {
return column.formatter(value, data);
}
return value;
}
const { display, format, digitsInfo, currencyCode, timezone } = column.pipeArgs;
const locale = column.grid.locale;
switch (column.dataType) {
case GridColumnDataType.Date:
case GridColumnDataType.DateTime:
case GridColumnDataType.Time:
return formatDate(value, format, locale, timezone);
case GridColumnDataType.Currency:
return formatCurrency(value, currencyCode || getLocaleCurrencyCode(locale), display, digitsInfo, locale);
case GridColumnDataType.Number:
return formatNumber(value, locale, digitsInfo);
case GridColumnDataType.Percent:
return formatPercent(value, locale, digitsInfo);
default:
return value;
}
}
protected getUniqueFilterItems(column: ColumnType, filterItems: IgxFilterItem[]) {
const filteredUniqueValues = filterItems.reduce((map, item) => {
let key = item.value;
if (column.dataType === GridColumnDataType.String && column.filteringIgnoreCase) {
key = key?.toString().toLowerCase();
} else if (column.dataType === GridColumnDataType.DateTime) {
key = item.value?.toString();
item.value = key ? new Date(key) : key;
} else if (column.dataType === GridColumnDataType.Time) {
const date = key ? new Date(key) : key;
key = date ? new Date().setHours(date.getHours(), date.getMinutes(), date.getSeconds()) : key;
item.value = key ? new Date(key) : key;
} else if (column.dataType === GridColumnDataType.Date) {
const date = key ? new Date(key) : key;
key = date ? new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString() : key;
item.value = date;
}
return map.has(key) ? map : map.set(key, item)
}, new Map());
const uniqueValues = Array.from(filteredUniqueValues.values());
return uniqueValues;
}
protected shouldFormatFilterValues(_column: ColumnType): boolean {
return false;
}
public abstract filter(data: any[], expressionsTree: IFilteringExpressionsTree,
advancedExpressionsTree?: IFilteringExpressionsTree, grid?: GridType): any[];
protected abstract getFieldValue(rec: any, fieldName: string, isDate?: boolean, isTime?: boolean, grid?: GridType): any;
}
export class NoopFilteringStrategy extends BaseFilteringStrategy {
protected getFieldValue(rec: any, _fieldName: string) {
return rec;
}
private static _instance: NoopFilteringStrategy = null;
public static instance() {
return this._instance || (this._instance = new NoopFilteringStrategy());
}
public filter(data: any[], _: IFilteringExpressionsTree, __?: IFilteringExpressionsTree): any[] {
return data;
}
}
export class FilteringStrategy extends BaseFilteringStrategy {
private static _instance: FilteringStrategy = null;
constructor() {
super();
}
public static instance() {
return this._instance || (this._instance = new this());
}
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree,
grid: GridType): T[] {
let i;
let rec;
const len = data.length;
const res: T[] = [];
if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) {
return data;
}
for (i = 0; i < len; i++) {
rec = data[i];
if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
res.push(rec);
}
}
return res;
}
protected getFieldValue(rec: any, fieldName: string, isDate = false, isTime = false, grid?: GridType): any {
const column = grid?.getColumnByName(fieldName);
let value = resolveNestedPath(rec, fieldName);
value = column?.formatter && this.shouldFormatFilterValues(column) ?
column.formatter(value, rec) :
value && (isDate || isTime) ? parseDate(value) : value;
return value;
}
}
export class FormattedValuesFilteringStrategy extends FilteringStrategy {
/**
* Creates a new instance of FormattedValuesFilteringStrategy.
*
* @param fields An array of column field names that should be formatted.
* If omitted the values of all columns which has formatter will be formatted.
*/
constructor(private fields?: string[]) {
super();
}
protected override shouldFormatFilterValues(column: ColumnType): boolean {
return !this.fields || this.fields.length === 0 || this.fields.some(f => f === column.field);
}
}