Skip to content

Commit 1419ff3

Browse files
authored
Merge pull request #7791 from IgniteUI/dmdimitrov/exportersPerf-10.0.x
fix(exporters): performance optimizations for CSV and Excel exporters
2 parents 85d024c + 7f3a63c commit 1419ff3

17 files changed

+244
-57
lines changed

package-lock.json

Lines changed: 6 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@
5757
"@types/source-map": "0.5.2",
5858
"classlist.js": "^1.1.20150312",
5959
"core-js": "^2.6.11",
60+
"core-js-pure": "^3.6.5",
6061
"hammerjs": "^2.0.8",
6162
"igniteui-trial-watermark": "^1.0.3",
6263
"jszip": "^3.4.0",
6364
"resize-observer-polyfill": "^1.5.1",
6465
"rxjs": "^6.5.4",
6566
"tslib": "^2.0.0",
67+
"setimmediate": "^1.0.5",
6668
"web-animations-js": "^2.3.2",
6769
"zone.js": "~0.10.3"
6870
},

projects/igniteui-angular/ng-package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"whitelistedNonPeerDependencies": [
99
"@types/hammerjs",
1010
"@types/jszip",
11+
"core-js-pure",
1112
"hammerjs",
1213
"jszip",
1314
"resize-observer-polyfill",

projects/igniteui-angular/ng-package.prod.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"whitelistedNonPeerDependencies": [
1111
"@types/hammerjs",
1212
"@types/jszip",
13+
"core-js-pure",
1314
"hammerjs",
1415
"jszip",
1516
"resize-observer-polyfill",

projects/igniteui-angular/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"dependencies": {
6969
"@types/hammerjs": "^2.0.36",
7070
"@types/jszip": "^3.1.7",
71+
"core-js-pure": "^3.6.5",
7172
"hammerjs": "^2.0.8",
7273
"jszip": "^3.3.0",
7374
"tslib": "^2.0.0",

projects/igniteui-angular/schematics/utils/dependency-handler.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const DEPENDENCIES_MAP: PackageEntry[] = [
3030
{ name: '@types/hammerjs', target: PackageTarget.DEV },
3131
{ name: '@types/jszip', target: PackageTarget.DEV },
3232
{ name: 'igniteui-trial-watermark', target: PackageTarget.NONE },
33+
{ name: 'core-js-pure', target: PackageTarget.NONE },
3334
// peerDependencies
3435
{ name: '@angular/forms', target: PackageTarget.NONE },
3536
{ name: '@angular/common', target: PackageTarget.NONE },

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
22
import { isPlatformBrowser } from '@angular/common';
33
import { Observable } from 'rxjs';
44
import ResizeObserver from 'resize-observer-polyfill';
5+
import { setImmediate } from 'core-js-pure';
56

67
/**
78
* @hidden
@@ -383,3 +384,19 @@ export function compareMaps(map1: Map<any, any>, map2: Map<any, any>): boolean {
383384
}
384385
return match;
385386
}
387+
388+
export function yieldingLoop(count: number, chunkSize: number, callback: (index: number) => void, done: () => void) {
389+
let i = 0;
390+
const chunk = () => {
391+
const end = Math.min(i + chunkSize, count);
392+
for ( ; i < end; ++i) {
393+
callback(i);
394+
}
395+
if (i < count) {
396+
setImmediate(chunk);
397+
} else {
398+
done();
399+
}
400+
};
401+
chunk();
402+
}

projects/igniteui-angular/src/lib/services/csv/char-separated-value-data.ts

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ExportUtilities } from '../exporter-common/export-utilities';
2+
import { yieldingLoop } from '../../core/utils';
23

34
/**
45
* @hidden
@@ -36,6 +37,26 @@ export class CharSeparatedValueData {
3637
return this._headerRecord + this._dataRecords;
3738
}
3839

40+
public prepareDataAsync(done: (result: string) => void) {
41+
if (!this._data || this._data.length === 0) {
42+
done('');
43+
}
44+
45+
const keys = ExportUtilities.getKeysFromData(this._data);
46+
47+
if (keys.length === 0) {
48+
done('');
49+
}
50+
51+
this._isSpecialData = ExportUtilities.isSpecialData(this._data);
52+
this._escapeCharacters.push(this._delimiter);
53+
54+
this._headerRecord = this.processHeaderRecord(keys, this._escapeCharacters);
55+
this.processDataRecordsAsync(this._data, keys, this._escapeCharacters, (dr) => {
56+
done(this._headerRecord + dr);
57+
});
58+
}
59+
3960
private processField(value, escapeChars): string {
4061
let safeValue = ExportUtilities.hasValue(value) ? String(value) : '';
4162
if (escapeChars.some((v) => safeValue.includes(v))) {
@@ -54,23 +75,37 @@ export class CharSeparatedValueData {
5475
}
5576

5677
private processRecord(record, keys, escapeChars): string {
57-
let recordData = '';
58-
for (const keyName of keys) {
59-
60-
const value = (record[keyName] !== undefined) ? record[keyName] : this._isSpecialData ? record : '';
61-
recordData += this.processField(value, this._escapeCharacters);
78+
const recordData = new Array(keys.length);
79+
for (let index = 0; index < keys.length; index++) {
80+
const value = (record[keys[index]] !== undefined) ? record[keys[index]] : this._isSpecialData ? record : '';
81+
recordData[index] = this.processField(value, this._escapeCharacters);
6282
}
6383

64-
return recordData.slice(0, -this._delimiterLength) + this._eor;
84+
return recordData.join('').slice(0, -this._delimiterLength) + this._eor;
6585
}
6686

6787
private processDataRecords(currentData, keys, escapeChars) {
68-
let dataRecords = '';
69-
for (const row of currentData) {
70-
dataRecords += this.processRecord(row, keys, escapeChars);
88+
const dataRecords = new Array(currentData.length);
89+
90+
for (let i = 0; i < currentData.length; i++) {
91+
const row = currentData[i];
92+
dataRecords[i] = this.processRecord(row, keys, escapeChars);
7193
}
7294

73-
return dataRecords;
95+
return dataRecords.join('');
96+
}
97+
98+
private processDataRecordsAsync(currentData, keys, escapeChars, done: (result: string) => void) {
99+
const dataRecords = new Array(currentData.length);
100+
101+
yieldingLoop(currentData.length, 1000,
102+
(i) => {
103+
const row = currentData[i];
104+
dataRecords[i] = this.processRecord(row, keys, escapeChars);
105+
},
106+
() => {
107+
done(dataRecords.join(''));
108+
});
74109
}
75110

76111
private setDelimiter(value) {

projects/igniteui-angular/src/lib/services/csv/csv-exporter.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@ export class IgxCsvExporterService extends IgxBaseExporter {
5050
protected exportDataImplementation(data: any[], options: IgxCsvExporterOptions) {
5151
data = data.map((item) => item.rowData);
5252
const csvData = new CharSeparatedValueData(data, options.valueDelimiter);
53-
this._stringData = csvData.prepareData();
54-
55-
this.saveFile(options);
56-
this.onExportEnded.emit({ csvData: this._stringData });
53+
csvData.prepareDataAsync((r) => {
54+
this._stringData = r;
55+
this.saveFile(options);
56+
this.onExportEnded.emit({ csvData: this._stringData });
57+
});
5758
}
5859

5960
private saveFile(options: IgxCsvExporterOptions) {

projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,14 +266,13 @@ describe('Excel Exporter', () => {
266266
const grid = fix.componentInstance.grid;
267267
grid.columns[1].hidden = true;
268268
grid.columns[2].hidden = true;
269-
const columnWidths = [100, 200, 0, undefined, null];
269+
const columnWidths = [100, 200, 0, null];
270270
fix.detectChanges();
271271

272272
await setColWidthAndExport(grid, options, fix, columnWidths[0]);
273273
await setColWidthAndExport(grid, options, fix, columnWidths[1]);
274274
await setColWidthAndExport(grid, options, fix, columnWidths[2]);
275275
await setColWidthAndExport(grid, options, fix, columnWidths[3]);
276-
await setColWidthAndExport(grid, options, fix, columnWidths[4]);
277276
});
278277

279278
it('should export all rows with the height specified in options.', async () => {

0 commit comments

Comments
 (0)