Skip to content

Commit c71bc6e

Browse files
authored
Merge pull request #15871 from IgniteUI/19.2.x
perf(utils): Improved internal utils performance (#15816)
2 parents b4aad51 + 35b154c commit c71bc6e

File tree

13 files changed

+206
-208
lines changed

13 files changed

+206
-208
lines changed

projects/igniteui-angular/src/lib/core/utils.ts

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { mergeWith } from 'lodash-es';
44
import { NEVER, Observable } from 'rxjs';
55
import { setImmediate } from './setImmediate';
66
import { isDevMode } from '@angular/core';
7-
import { IgxTheme } from '../services/theme/theme.token';
7+
import type { IgxTheme } from '../services/theme/theme.token';
88

99
/** @hidden @internal */
1010
export const ELEMENTS_TOKEN = /*@__PURE__*/new InjectionToken<boolean>('elements environment');
@@ -31,17 +31,9 @@ export const getResizeObserver = () => globalThis.window?.ResizeObserver;
3131
/**
3232
* @hidden
3333
*/
34-
export const cloneArray = (array: any[], deep?: boolean) => {
35-
const arr = [];
36-
if (!array) {
37-
return arr;
38-
}
39-
let i = array.length;
40-
while (i--) {
41-
arr[i] = deep ? cloneValue(array[i]) : array[i];
42-
}
43-
return arr;
44-
};
34+
export function cloneArray<T>(array: T[], deep = false): T[] {
35+
return deep ? (array ?? []).map(cloneValue) : (array ?? []).slice();
36+
}
4537

4638
/**
4739
* Doesn't clone leaf items
@@ -108,7 +100,7 @@ export const cloneValue = (value: any): any => {
108100
return new Date(value.getTime());
109101
}
110102
if (Array.isArray(value)) {
111-
return [...value];
103+
return value.slice();
112104
}
113105

114106
if (value instanceof Map || value instanceof Set) {
@@ -298,7 +290,7 @@ export class PlatformUtil {
298290
*/
299291
public getNodeSizeViaRange(range: Range, node: HTMLElement, sizeHoldingNode?: HTMLElement) {
300292
let overflow = null;
301-
let nodeStyles;
293+
let nodeStyles: string[];
302294

303295
if (!this.isFirefox) {
304296
overflow = node.style.overflow;
@@ -455,17 +447,17 @@ export const resizeObservable = (target: HTMLElement): Observable<ResizeObserver
455447
// check whether we are on server env or client env
456448
if (resizeObserver) {
457449
return new Observable((observer) => {
458-
const instance = new resizeObserver((entries: ResizeObserverEntry[]) => {
459-
observer.next(entries);
460-
});
461-
instance.observe(target);
462-
const unsubscribe = () => instance.disconnect();
463-
return unsubscribe;
450+
const instance = new resizeObserver((entries: ResizeObserverEntry[]) => {
451+
observer.next(entries);
452+
});
453+
instance.observe(target);
454+
const unsubscribe = () => instance.disconnect();
455+
return unsubscribe;
464456
});
465-
} else {
466-
// if on a server env return a empty observable that does not complete immediately
467-
return NEVER;
468457
}
458+
// if on a server env return a empty observable that does not complete immediately
459+
return NEVER;
460+
469461
}
470462

471463
/**
@@ -476,7 +468,7 @@ export const resizeObservable = (target: HTMLElement): Observable<ResizeObserver
476468
*/
477469
export const compareMaps = (map1: Map<any, any>, map2: Map<any, any>): boolean => {
478470
if (!map2) {
479-
return !map1 ? true : false;
471+
return !map1;
480472
}
481473
if (map1.size !== map2.size) {
482474
return false;
@@ -496,26 +488,38 @@ export const compareMaps = (map1: Map<any, any>, map2: Map<any, any>): boolean =
496488
return match;
497489
};
498490

491+
function _isObject(entity: unknown): entity is object {
492+
return entity != null && typeof entity === 'object';
493+
}
494+
495+
export function columnFieldPath(path?: string): string[] {
496+
return path?.split('.') ?? [];
497+
}
498+
499499
/**
500-
*
501500
* Given a property access path in the format `x.y.z` resolves and returns
502501
* the value of the `z` property in the passed object.
503502
*
504503
* @hidden
505504
* @internal
506505
*/
507-
export const resolveNestedPath = (obj: any, path: string) => {
508-
const parts = path?.split('.') ?? [];
509-
let current = obj[parts.shift()];
506+
export function resolveNestedPath<T extends object, U>(obj: unknown, pathParts: string[], defaultValue?: U): T | U | undefined {
507+
if (!_isObject(obj) || pathParts.length < 1) {
508+
return defaultValue;
509+
}
510510

511-
parts.forEach(prop => {
512-
if (current) {
513-
current = current[prop];
511+
let current = obj;
512+
513+
for (const key of pathParts) {
514+
if (_isObject(current) && key in (current as T)) {
515+
current = current[key];
516+
} else {
517+
return defaultValue;
514518
}
515-
});
519+
}
516520

517-
return current;
518-
};
521+
return current as T;
522+
}
519523

520524
/**
521525
*
@@ -628,12 +632,12 @@ export function modulo(n: number, d: number) {
628632
* ```
629633
*/
630634
export function* intoChunks<T>(arr: T[], size: number) {
631-
if (size < 1) {
632-
throw new Error('size must be an integer >= 1');
633-
}
634-
for (let i = 0; i < arr.length; i += size) {
635-
yield arr.slice(i, i + size);
636-
}
635+
if (size < 1) {
636+
throw new Error('size must be an integer >= 1');
637+
}
638+
for (let i = 0; i < arr.length; i += size) {
639+
yield arr.slice(i, i + size);
640+
}
637641
}
638642

639643
/**
@@ -646,7 +650,6 @@ export function getComponentCssSizeVar(size: string) {
646650
return 'var(--ig-size, var(--ig-size-small))';
647651
case "2":
648652
return 'var(--ig-size, var(--ig-size-medium))';
649-
case "3":
650653
default:
651654
return 'var(--ig-size, var(--ig-size-large))';
652655
}
@@ -662,9 +665,9 @@ export function normalizeURI(path: string) {
662665

663666
export function getComponentTheme(el: Element) {
664667
return globalThis.window
665-
?.getComputedStyle(el)
666-
.getPropertyValue('--theme')
667-
.trim() as IgxTheme;
668+
?.getComputedStyle(el)
669+
.getPropertyValue('--theme')
670+
.trim() as IgxTheme;
668671
}
669672

670673
/**

projects/igniteui-angular/src/lib/data-operations/filtering-strategy.ts

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
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, EntityType, GridType } from '../grids/common/grid.interface';
1+
import { FilteringLogic, type IFilteringExpression } from './filtering-expression.interface';
2+
import { FilteringExpressionsTree, type IFilteringExpressionsTree } from './filtering-expressions-tree';
3+
import { resolveNestedPath, parseDate, formatDate, formatCurrency, columnFieldPath } from '../core/utils';
4+
import type { ColumnType, EntityType, GridType } from '../grids/common/grid.interface';
55
import { GridColumnDataType } from './data-util';
66
import { SortingDirection } from './sorting-strategy';
77
import { formatNumber, formatPercent, getLocaleCurrencyCode } from '@angular/common';
8-
import { IFilteringState } from './filtering-state.interface';
8+
import type { IFilteringState } from './filtering-state.interface';
99
import { isTree } from './expressions-tree-util';
10-
import { IgxHierarchicalGridComponent } from '../grids/hierarchical-grid/hierarchical-grid.component';
10+
import type { IgxHierarchicalGridComponent } from '../grids/hierarchical-grid/hierarchical-grid.component';
1111

1212
const DateType = 'date';
1313
const DateTimeType = 'dateTime';
@@ -26,7 +26,7 @@ export interface IFilteringStrategy {
2626
filter(data: any[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree?: IFilteringExpressionsTree,
2727
grid?: GridType): any[];
2828
/* csSuppress */
29-
getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree) : Promise<IgxFilterItem[]>;
29+
getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise<IgxFilterItem[]>;
3030
}
3131

3232
/* csSuppress */
@@ -37,16 +37,14 @@ export interface IgxFilterItem {
3737
}
3838

3939
/* csSuppress */
40-
export abstract class BaseFilteringStrategy implements IFilteringStrategy {
40+
export abstract class BaseFilteringStrategy implements IFilteringStrategy {
4141
// protected
4242
public findMatchByExpression(rec: any, expr: IFilteringExpression, isDate?: boolean, isTime?: boolean, grid?: GridType): boolean {
4343
if (expr.searchTree) {
4444
const records = rec[expr.searchTree.entity];
4545
const shouldMatchRecords = expr.conditionName === 'inQuery';
4646
if (!records) { // child grid is not yet created
4747
return true;
48-
} else if (records.length === 0) { // child grid is empty
49-
return false;
5048
}
5149

5250
for (let index = 0; index < records.length; index++) {
@@ -74,7 +72,7 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy {
7472
const operator = expressionsTree.operator as FilteringLogic;
7573
let matchOperand;
7674

77-
if (expressionsTree.filteringOperands && expressionsTree.filteringOperands.length) {
75+
if (expressionsTree.filteringOperands?.length) {
7876
for (const operand of expressionsTree.filteringOperands) {
7977
matchOperand = this.matchRecord(rec, operand, grid, entity);
8078

@@ -132,19 +130,18 @@ export abstract class BaseFilteringStrategy implements IFilteringStrategy {
132130
}
133131

134132
public getFilterItems(column: ColumnType, tree: IFilteringExpressionsTree): Promise<IgxFilterItem[]> {
133+
const applyFormatter = column.formatter && this.shouldFormatFilterValues(column);
135134

136135
let data = column.grid.gridAPI.filterDataByExpressions(tree);
137136
data = column.grid.gridAPI.sortDataByExpressions(data,
138137
[{ fieldName: column.field, dir: SortingDirection.Asc, ignoreCase: column.sortingIgnoreCase }]);
139138

140-
const columnField = column.field;
141-
let filterItems: IgxFilterItem[] = data.map(record => {
142-
let value = resolveNestedPath(record, columnField);
143-
const applyFormatter = column.formatter && this.shouldFormatFilterValues(column);
144139

145-
value = applyFormatter ?
146-
column.formatter(value, record) :
147-
value;
140+
const pathParts = columnFieldPath(column.field)
141+
let filterItems: IgxFilterItem[] = data.map(record => {
142+
const value = applyFormatter ?
143+
column.formatter(resolveNestedPath(record, pathParts), record) :
144+
resolveNestedPath(record, pathParts);
148145

149146
return {
150147
value,
@@ -239,36 +236,25 @@ export class NoopFilteringStrategy extends BaseFilteringStrategy {
239236
export class FilteringStrategy extends BaseFilteringStrategy {
240237
private static _instance: FilteringStrategy = null;
241238

242-
constructor() {
243-
super();
244-
}
245239

246240
public static instance() {
247241
return this._instance || (this._instance = new this());
248242
}
249243

250244
public filter<T>(data: T[], expressionsTree: IFilteringExpressionsTree, advancedExpressionsTree: IFilteringExpressionsTree,
251245
grid: GridType): T[] {
252-
let i;
253-
let rec;
254-
const len = data.length;
255-
const res: T[] = [];
256246

257-
if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree)) || !len) {
247+
248+
if ((FilteringExpressionsTree.empty(expressionsTree) && FilteringExpressionsTree.empty(advancedExpressionsTree))) {
258249
return data;
259250
}
260-
for (i = 0; i < len; i++) {
261-
rec = data[i];
262-
if (this.matchRecord(rec, expressionsTree, grid) && this.matchRecord(rec, advancedExpressionsTree, grid)) {
263-
res.push(rec);
264-
}
265-
}
266-
return res;
251+
252+
return data.filter(record => this.matchRecord(record, expressionsTree, grid) && this.matchRecord(record, advancedExpressionsTree, grid));
267253
}
268254

269255
protected getFieldValue(rec: any, fieldName: string, isDate = false, isTime = false, grid?: GridType): any {
270256
const column = grid?.getColumnByName(fieldName);
271-
let value = resolveNestedPath(rec, fieldName);
257+
let value = resolveNestedPath(rec, columnFieldPath(fieldName));
272258

273259
value = column?.formatter && this.shouldFormatFilterValues(column) ?
274260
column.formatter(value, rec) :

projects/igniteui-angular/src/lib/grids/common/pipes.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Pipe, PipeTransform, Inject } from '@angular/core';
22
import { DataUtil } from '../../data-operations/data-util';
3-
import { cloneArray, resolveNestedPath } from '../../core/utils';
3+
import { cloneArray, columnFieldPath, resolveNestedPath } from '../../core/utils';
44
import { GridType, IGX_GRID_BASE, RowType } from './grid.interface';
55
import { IgxAddRow } from './crud.service';
66
import { IgxSummaryOperand, IgxSummaryResult } from '../summaries/grid-summary';
@@ -26,11 +26,12 @@ export class IgxGridCellStyleClassesPipe implements PipeTransform {
2626
}
2727

2828
const result = [];
29+
const pathParts = columnFieldPath(field);
2930

3031
for (const cssClass of Object.keys(cssClasses)) {
3132
const callbackOrValue = cssClasses[cssClass];
3233
const apply = typeof callbackOrValue === 'function' ?
33-
callbackOrValue(data, field, resolveNestedPath(data, field), index) : callbackOrValue;
34+
callbackOrValue(data, field, resolveNestedPath(data, pathParts), index) : callbackOrValue;
3435
if (apply) {
3536
result.push(cssClass);
3637
}
@@ -57,9 +58,11 @@ export class IgxGridCellStylesPipe implements PipeTransform {
5758
return css;
5859
}
5960

61+
const pathParts = columnFieldPath(field);
62+
6063
for (const prop of Object.keys(styles)) {
6164
const res = styles[prop];
62-
css[prop] = typeof res === 'function' ? res(data, field, resolveNestedPath(data, field), index) : res;
65+
css[prop] = typeof res === 'function' ? res(data, field, resolveNestedPath(data, pathParts), index) : res;
6366
}
6467

6568
return css;
@@ -70,7 +73,7 @@ export class IgxGridCellStylesPipe implements PipeTransform {
7073
* @hidden
7174
* @internal
7275
*/
73-
@Pipe({
76+
@Pipe({
7477
name: 'igxCellImageAlt',
7578
standalone: true
7679
})
@@ -116,7 +119,7 @@ export class IgxGridRowClassesPipe implements PipeTransform {
116119
_rowData: any,
117120
_: number
118121
) {
119-
const result = new Set(['igx-grid__tr', index % 2 ? 'igx-grid__tr--even': 'igx-grid__tr--odd']);
122+
const result = new Set(['igx-grid__tr', index % 2 ? 'igx-grid__tr--even' : 'igx-grid__tr--odd']);
120123
const mapping = [
121124
[selected, 'igx-grid__tr--selected'],
122125
[editMode, 'igx-grid__tr--edit'],
@@ -329,7 +332,7 @@ export class IgxGridRowPinningPipe implements PipeTransform {
329332
export class IgxGridDataMapperPipe implements PipeTransform {
330333

331334
public transform(data: any[], field: string, _: number, val: any, isNestedPath: boolean) {
332-
return isNestedPath ? resolveNestedPath(data, field) : val;
335+
return isNestedPath ? resolveNestedPath(data, columnFieldPath(field)) : val;
333336
}
334337
}
335338

@@ -354,13 +357,13 @@ export class IgxGridTransactionStatePipe implements PipeTransform {
354357
if (rowEditable) {
355358
const rowCurrentState = transactions.getAggregatedValue(row_id, false);
356359
if (rowCurrentState) {
357-
const value = resolveNestedPath(rowCurrentState, field);
360+
const value = resolveNestedPath(rowCurrentState, columnFieldPath(field));
358361
return value !== undefined && value !== null;
359362
}
360363
} else {
361364
const transaction = transactions.getState(row_id);
362-
const value = resolveNestedPath(transaction?.value ?? {}, field);
363-
return transaction && transaction.value && (value || value === 0 || value === false);
365+
const value = resolveNestedPath(transaction?.value ?? {}, columnFieldPath(field));
366+
return transaction?.value && (value || value === 0 || value === false);
364367
}
365368
}
366369
}
@@ -371,7 +374,7 @@ export class IgxGridTransactionStatePipe implements PipeTransform {
371374
})
372375
export class IgxColumnFormatterPipe implements PipeTransform {
373376

374-
public transform(value: any, formatter: (v: any, data: any, columnData? :any) => any, rowData: any, columnData? : any) {
377+
public transform(value: any, formatter: (v: any, data: any, columnData?: any) => any, rowData: any, columnData?: any) {
375378
return formatter(value, rowData, columnData);
376379
}
377380
}

0 commit comments

Comments
 (0)