diff --git a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts
index 9bc777b88b5..b3c808d2e3f 100644
--- a/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts
+++ b/projects/igniteui-angular/src/lib/services/excel/excel-exporter-grid.spec.ts
@@ -48,8 +48,8 @@ import { IgxHierarchicalGridExportComponent,
import { IgxHierarchicalGridComponent } from '../../grids/hierarchical-grid/public_api';
import { IgxHierarchicalRowComponent } from '../../grids/hierarchical-grid/hierarchical-row.component';
import { GridFunctions } from '../../test-utils/grid-functions.spec';
-import { IgxPivotGridMultipleRowComponent, IgxPivotGridTestComplexHierarchyComponent } from '../../test-utils/pivot-grid-samples.spec';
-import { IgxPivotGridComponent, PivotRowLayoutType } from '../../grids/pivot-grid/public_api';
+import { IgxPivotGridMultipleRowComponent, IgxPivotGridTestComplexHierarchyComponent, SALES_DATA } from '../../test-utils/pivot-grid-samples.spec';
+import { IgxPivotGridComponent, IgxPivotNumericAggregate, PivotRowLayoutType } from '../../grids/pivot-grid/public_api';
describe('Excel Exporter', () => {
configureTestSuite();
@@ -1458,7 +1458,7 @@ describe('Excel Exporter', () => {
let grid: IgxPivotGridComponent;
beforeEach(waitForAsync(() => {
- options = createExportOptions('PivotGridGridExcelExport');
+ options = createExportOptions('PivotGridExcelExport');
}));
it('should export pivot grid', async () => {
@@ -1483,6 +1483,80 @@ describe('Excel Exporter', () => {
await exportAndVerify(grid, options, actualData.exportPivotGridDataWithHeaders, false);
});
+ it('should export pivot grid with hierarchical row dimensions.', async () => {
+ fix = TestBed.createComponent(IgxPivotGridMultipleRowComponent);
+ fix.detectChanges();
+
+ grid = fix.componentInstance.pivotGrid;
+ fix.componentInstance.data = SALES_DATA;
+ fix.componentInstance.pivotConfigHierarchy = {
+ rows: [
+ {
+ memberName: 'All_Srep Code Alts',
+ enabled: true,
+ width: '150px',
+ childLevel: {
+ memberName: 'SREP_CODE_ALT',
+ displayName: 'Srep Code Alt',
+ sortDirection: 1,
+ enabled: true,
+ },
+ },
+ {
+ memberName: 'All_Srep Codes',
+ enabled: true,
+ width: '150px',
+ childLevel: {
+ memberName: 'SREP_CODE',
+ displayName: 'Srep Code',
+ sortDirection: 1,
+ enabled: true,
+ },
+ },
+ {
+ memberName: 'All_Customers',
+ enabled: true,
+ width: '150px',
+ childLevel: {
+ memberName: 'CUST_CODE',
+ displayName: 'Customer',
+ sortDirection: 1,
+ enabled: true,
+ },
+ }
+ ],
+ columns: [],
+ values: [
+ {
+ member: 'JOBS',
+ aggregate: {
+ key: 'Count of Jobs',
+ aggregator: IgxPivotNumericAggregate.count,
+ label: 'Count of Jobs',
+ },
+ enabled: true,
+ dataType: 'number',
+ },
+ {
+ member: 'INV_SALES',
+ aggregate: {
+ key: 'Sum of Sales',
+ aggregator: IgxPivotNumericAggregate.sum,
+ label: 'Sum of Sales',
+ },
+ enabled: true,
+ dataType: 'number',
+ },
+ ],
+ filters: [],
+ };
+ grid.pivotUI.showRowHeaders = true;
+ fix.detectChanges();
+ await wait(300);
+
+ await exportAndVerify(grid, options, actualData.exportPivotGridHierarchicalRowDimensions, false);
+ });
+
it('should export hierarchical pivot grid', async () => {
fix = TestBed.createComponent(IgxPivotGridTestComplexHierarchyComponent);
fix.detectChanges();
diff --git a/projects/igniteui-angular/src/lib/services/excel/excel-files.ts b/projects/igniteui-angular/src/lib/services/excel/excel-files.ts
index 6b7c9fb512f..95f41241b01 100644
--- a/projects/igniteui-angular/src/lib/services/excel/excel-files.ts
+++ b/projects/igniteui-angular/src/lib/services/excel/excel-files.ts
@@ -632,7 +632,7 @@ export class WorksheetFile implements IExcelFile {
for (const currentCol of headersForLevel) {
const spanLength = isVertical ? currentCol.rowSpan : currentCol.columnSpan;
- if (currentCol.level === i && currentCol.headerType !== ExportHeaderType.PivotMergedHeader) {
+ if (currentCol.level === i) {
let columnCoordinate;
const column = isVertical
? this.rowIndex
@@ -644,7 +644,10 @@ export class WorksheetFile implements IExcelFile {
if (currentCol.headerType === ExportHeaderType.PivotRowHeader) {
rowCoordinate = startValue + 1;
}
- const columnValue = dictionary.saveValue(currentCol.header, true, false);
+
+ const columnValue = currentCol.headerType === ExportHeaderType.PivotMergedHeader ?
+ dictionary.saveValue(currentCol.field, true, true) :
+ dictionary.saveValue(currentCol.header, true, false);
columnCoordinate = (currentCol.field === GRID_LEVEL_COL
? ExcelStrings.getExcelColumn(worksheetData.columnCount + 1)
diff --git a/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts b/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts
index d03e1bd864b..708212fad86 100644
--- a/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts
+++ b/projects/igniteui-angular/src/lib/services/excel/test-data.service.spec.ts
@@ -1659,7 +1659,7 @@ export class FileContentData {
public get exportPivotGridData() {
this._sharedStringsData =
- `count="38" uniqueCount="23">AccessoriesBikesClothingComponentsUSAUruguayBulgaria04/07/202101/06/202001/05/201905/12/202001/01/202102/19/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
+ `count="38" uniqueCount="23">AccessoriesBikesClothingComponentsUSAUruguayBulgaria04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
this._worksheetData =
`
@@ -1667,14 +1667,14 @@ export class FileContentData {
topLeftCell="D1" activePane="topRight" state="frozen"/>
- 14151617181920
2122212221222122212221222122
04729385.58
158683.56
24928212.81
51049216.05
61129649.57
1245668.33
341324018.13
`;
+ 14151617181920
2122212221222122212221222122
04729385.58
158683.56
26928212.81
1049216.05
41129649.57
51245668.33
341324018.13
`;
return this.createData();
}
public get exportPivotGridDataWithHeaders() {
this._sharedStringsData =
- `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/05/201905/12/202001/01/202102/19/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
+ `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
this._worksheetData =
`
@@ -1682,17 +1682,17 @@ export class FileContentData {
topLeftCell="D1" activePane="topRight" state="frozen"/>
- 05917181920212223
2425242524252425242524252425
161029385.58
2711683.56
361228212.81
71349216.05
81429649.57
1545668.33
461624018.13
`;
+ 05917181920212223
2425242524252425242524252425
161029385.58
2711683.56
381228212.81
1349216.05
61429649.57
71545668.33
461624018.13
`;
return this.createData();
}
public get exportPivotGridDataHorizontal() {
this._sharedStringsData =
- `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/05/201905/12/202001/01/202102/19/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
+ `count="41" uniqueCount="26">ProductCategoryAccessoriesBikesClothingComponentsCountryUSAUruguayBulgariaDate04/07/202101/06/202001/01/202102/19/202001/05/201905/12/202012/08/2021StanleyElisaLydiaDavidJohnLarryWalterUnitsSoldUnitPrice`;
this._worksheetData =
- `059171819202122232425242524252425242524252425161029385.582711683.56361228212.8171349216.0581429649.571545668.33461624018.13`;
+ `05917181920212223
2425242524252425242524252425
161029385.58
2711683.56
381228212.81
1349216.05
61429649.57
71545668.33
461624018.13
`;
return this.createData();
}
@@ -1712,6 +1712,25 @@ export class FileContentData {
return this.createData();
}
+ public get exportPivotGridHierarchicalRowDimensions() {
+ this._sharedStringsData =
+ `count="198" uniqueCount="56">All_Srep Code Alts006020024029037041047053056060All_Srep Codes538254256162183103159603604622410419421263110111220231238All_Customers45230578001555780033864VS00862CW9211800185678046621804969950535965049856800332395044928148650596475062088VS05682053968203889110609831057802VS02524JOBSINV_SALES`;
+
+ this._worksheetData =
+ `
+
+
+
+
+ 012325455
11121733281.0299999999
3313253.31
34158081.95
35131745.11
36127565.64
37198731.63
38140424.75
39118538.62
40116352.73
41130413.87
42130797.93
43137109.5
44124370.78
45110792.43
4617229.73
471176455.66
4814418.15
49118192.16
50124663.14
51113113.94
5212497.11
53158532.89
13113253.31
3313253.31
141158081.95
34158081.95
151259310.75
35131745.11
36127565.64
161198731.63
37198731.63
171140424.75
38140424.75
181234891.35
39118538.62
40116352.73
191130413.87
41130413.87
201130797.93
42130797.93
211137109.5
43137109.5
221124370.78
` +
+ `44124370.78
231110792.43
45110792.43
24117229.73
4617229.73
2511176455.66
471176455.66
26114418.15
4814418.15
271118192.16
49118192.16
281124663.14
50124663.14
291113113.94
51113113.94
30112497.11
5212497.11
311158532.89
53158532.89
21113253.31
3313253.31
13113253.31
3313253.31
3113117392.7
34158081.95
35131745.11
36127565.64
141158081.95
34158081.95
151259310.75
35131745.11
36127565.64
4112139156.38
37198731.63
38140424.75
161198731.63
37198731.63
171140424.75
38140424.75
511234891.35
39118538.62
40116352.73
181234891.35
` +
+ `39118538.62
40116352.73
611130413.87
41130413.87
191130413.87
41130413.87
711392278.20999999999
42130797.93
43137109.5
44124370.78
201130797.93
42130797.93
211137109.5
43137109.5
221124370.78
44124370.78
8113194477.82
45110792.43
4617229.73
471176455.66
231110792.43
45110792.43
24117229.73
4617229.73
2511176455.66
471176455.66
91114418.15
4814418.15
26114418.15
4814418.15
1011242855.3
49118192.16
50124663.14
271118192.16
49118192.16
281124663.14
50124663.14
1111374143.94
51113113.94
5212497.11
53158532.89
291113113.94
` +
+ `51113113.94
30112497.11
5212497.11
311158532.89
53158532.89
`;
+
+ return this.createData();
+ }
+
public get exportGridWithSummaries() {
this._sharedStringsData =
`count="86" uniqueCount="36">CityShippedContactTitlePTODaysGRID_LEVEL_COLBerlinSales RepresentativeMéxico D.F.OwnerLondonLuleåOrder AdministratorMannheimStrasbourgMarketing ManagerMadridMarseilleTsawassenAccounting ManagerBuenos AiresSales AgentBernSao PauloSales AssociateAachenNantesGrazSales ManagerMarketing AssistantLilletrueAssistant Sales AgentBräckeMünchenTorino`;
diff --git a/projects/igniteui-angular/src/lib/services/excel/zip-verification-wrapper.spec.ts b/projects/igniteui-angular/src/lib/services/excel/zip-verification-wrapper.spec.ts
index 0206a6ebbdd..94e73010040 100644
--- a/projects/igniteui-angular/src/lib/services/excel/zip-verification-wrapper.spec.ts
+++ b/projects/igniteui-angular/src/lib/services/excel/zip-verification-wrapper.spec.ts
@@ -59,7 +59,7 @@ export class ZipWrapper {
private createFilesAndFolders(obj: Object, prefix: string) {
Object.keys(obj).forEach((key) => {
if (ArrayBuffer.isView(obj[key])) {
- this._files.set(`${prefix}${key}`, obj[key]);
+ this._files.set(`${prefix}${key}`, obj[key] as Uint8Array);
this._filesAndFolders.push(`${prefix}${key}`);
} else {
const newPrefix = `${prefix}${key}/`;
diff --git a/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts b/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts
index 189b4290623..55abad6726b 100644
--- a/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts
+++ b/projects/igniteui-angular/src/lib/services/exporter-common/base-export-service.ts
@@ -1336,36 +1336,60 @@ export abstract class IgxBaseExporter {
return;
}
- let startIndex = 0;
- const key = keys[0];
const records = this.flatRecords.map(r => r.data);
- const groupedRecords = {};
- records.forEach(obj => {
- const keyValue = obj[key.name];
- if (!groupedRecords[keyValue]) {
- groupedRecords[keyValue] = [];
- }
- groupedRecords[keyValue].push(obj);
- });
+ const groupedRecords = this.groupByKeys(records, keys);
- if (columnGroupParent) {
- const mapKeys = [...this.pivotGridKeyValueMap.keys()];
- const mapValues = [...this.pivotGridKeyValueMap.values()];
+ this.createRowDimension(groupedRecords, keys, columnGroupParent);
+ }
- for (const k of Object.keys(groupedRecords)) {
- groupedRecords[k] = groupedRecords[k].filter(row => mapKeys.every(mk => Object.keys(row).includes(mk))
- && mapValues.every(mv => Object.values(row).includes(mv)));
-
- if (groupedRecords[k].length === 0) {
- delete groupedRecords[k];
- }
+ private groupByKeys(items: any[], keys: any[]): any {
+ const group = (data: any[], groupKeys: any[]): any => {
+ if (groupKeys.length === 0) return data;
+
+ const newKeys = [...groupKeys];
+ const key = newKeys.shift().name;
+ const map = new Map();
+
+ for (const item of data) {
+ const keyValue = item[key];
+ if (!map.has(keyValue)) {
+ map.set(keyValue, []);
}
+ map.get(keyValue).push(item);
+ }
+
+ for (const [keyValue, value] of map) {
+ map.set(keyValue, group(value, newKeys));
+ }
+
+ return map;
+ };
+
+ return group(items, keys);
+ }
+
+ private calculateRowSpan(value: any): number {
+ if (value instanceof Map) {
+ return Array.from(value.values()).reduce(
+ (total, current) => total + this.calculateRowSpan(current),
+ 0
+ )
+ } else if (Array.isArray(value)) {
+ return value.length;
}
- for (const k of Object.keys(groupedRecords)) {
- let groupKey = k;
- const rowSpan = groupedRecords[k].length;
+ return 0;
+ }
+ private createRowDimension(node: any, keys: any[], columnGroupParent?: string) {
+ if (!(node instanceof Map)) return;
+
+ const key = keys[0];
+ const newKeys = keys.filter(k => k.level > key.level);
+ let startIndex = 0;
+ for (const k of node.keys()) {
+ let groupKey = k;
+ const rowSpan = this.calculateRowSpan(node.get(k));
const rowDimensionColumn: IColumnInfo = {
columnSpan: 1,
@@ -1377,32 +1401,28 @@ export abstract class IgxBaseExporter {
pinnedIndex: 0,
level: key.level,
dataType: 'string',
- headerType: groupedRecords[groupKey].length > 1 ? ExportHeaderType.MultiRowHeader : ExportHeaderType.RowHeader,
+ headerType: rowSpan > 1 ? ExportHeaderType.MultiRowHeader : ExportHeaderType.RowHeader,
};
- if (groupKey === 'undefined') {
- this.pivotGridColumns[this.pivotGridColumns.length - 1].columnSpan += 1;
+
+ if (!groupKey) {
+ // if (this.pivotGridColumns?.length)
+ // this.pivotGridColumns[this.pivotGridColumns.length - 1].columnSpan += 1;
rowDimensionColumn.headerType = ExportHeaderType.PivotMergedHeader;
groupKey = columnGroupParent;
}
- if (columnGroupParent) {
+ if (key.level > 0) {
rowDimensionColumn.columnGroupParent = columnGroupParent;
} else {
rowDimensionColumn.columnGroup = groupKey;
}
this.pivotGridColumns.push(rowDimensionColumn);
-
- if (keys.length > 1) {
- if (groupKey !== columnGroupParent) {
- this.pivotGridKeyValueMap.set(key.name, groupKey);
- }
- const newKeys = keys.filter(kdd => kdd !== key);
- this.preparePivotGridColumns(newKeys, groupKey)
- this.pivotGridKeyValueMap.delete(key.name);
- }
-
startIndex += rowSpan;
}
+
+ for (const k of node.keys()) {
+ this.createRowDimension(node.get(k), newKeys, columnGroupParent);
+ }
}
private addLevelColumns() {
diff --git a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts
index 4be80813f34..11bfd434ff6 100644
--- a/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts
+++ b/projects/igniteui-angular/src/lib/test-utils/pivot-grid-samples.spec.ts
@@ -400,3 +400,153 @@ export class IgxTotalSaleAggregate {
return max;
};
}
+
+export const SALES_DATA =[
+ {
+ "JOBS": 35,
+ "INV_SALES": 2497.11,
+ "CUST_CODE": "1057802",
+ "SREP_CODE": "231",
+ "SREP_CODE_ALT": "060"
+ },
+ {
+ "JOBS": 1241,
+ "INV_SALES": 98731.63,
+ "CUST_CODE": "CW9211",
+ "SREP_CODE": "162",
+ "SREP_CODE_ALT": "024"
+ },
+ {
+ "JOBS": 619,
+ "INV_SALES": 58532.89,
+ "CUST_CODE": "VS02524",
+ "SREP_CODE": "238",
+ "SREP_CODE_ALT": "060"
+ },
+ {
+ "JOBS": 534,
+ "INV_SALES": 37109.5,
+ "CUST_CODE": "80033239",
+ "SREP_CODE": "604",
+ "SREP_CODE_ALT": "041"
+ },
+ {
+ "JOBS": 262,
+ "INV_SALES": 16352.73,
+ "CUST_CODE": "8049699",
+ "SREP_CODE": "103",
+ "SREP_CODE_ALT": "029"
+ },
+ {
+ "JOBS": 1621,
+ "INV_SALES": 176455.66,
+ "CUST_CODE": "5062088",
+ "SREP_CODE": "421",
+ "SREP_CODE_ALT": "047"
+ },
+ {
+ "JOBS": 150,
+ "INV_SALES": 13113.94,
+ "CUST_CODE": "1060983",
+ "SREP_CODE": "220",
+ "SREP_CODE_ALT": "060"
+ },
+ {
+ "JOBS": 400,
+ "INV_SALES": 24663.14,
+ "CUST_CODE": "2038891",
+ "SREP_CODE": "111",
+ "SREP_CODE_ALT": "056"
+ },
+ {
+ "JOBS": 62,
+ "INV_SALES": 4418.15,
+ "CUST_CODE": "VS0568",
+ "SREP_CODE": "263",
+ "SREP_CODE_ALT": "053"
+ },
+ {
+ "JOBS": 128,
+ "INV_SALES": 10792.43,
+ "CUST_CODE": "1486",
+ "SREP_CODE": "410",
+ "SREP_CODE_ALT": "047"
+ },
+ {
+ "JOBS": 393,
+ "INV_SALES": 30797.93,
+ "CUST_CODE": "5049856",
+ "SREP_CODE": "603",
+ "SREP_CODE_ALT": "041"
+ },
+ {
+ "JOBS": 458,
+ "INV_SALES": 24370.78,
+ "CUST_CODE": "5044928",
+ "SREP_CODE": "622",
+ "SREP_CODE_ALT": "041"
+ },
+ {
+ "JOBS": 289,
+ "INV_SALES": 30413.87,
+ "CUST_CODE": "5053596",
+ "SREP_CODE": "159",
+ "SREP_CODE_ALT": "037"
+ },
+ {
+ "JOBS": 372,
+ "INV_SALES": 27565.64,
+ "CUST_CODE": "VS00862",
+ "SREP_CODE": "256",
+ "SREP_CODE_ALT": "020"
+ },
+ {
+ "JOBS": 354,
+ "INV_SALES": 40424.75,
+ "CUST_CODE": "80018567",
+ "SREP_CODE": "183",
+ "SREP_CODE_ALT": "024"
+ },
+ {
+ "JOBS": 356,
+ "INV_SALES": 31745.11,
+ "CUST_CODE": "80033864",
+ "SREP_CODE": "256",
+ "SREP_CODE_ALT": "020"
+ },
+ {
+ "JOBS": 910,
+ "INV_SALES": 58081.95,
+ "CUST_CODE": "80015557",
+ "SREP_CODE": "254",
+ "SREP_CODE_ALT": "020"
+ },
+ {
+ "JOBS": 166,
+ "INV_SALES": 7229.73,
+ "CUST_CODE": "5059647",
+ "SREP_CODE": "419",
+ "SREP_CODE_ALT": "047"
+ },
+ {
+ "JOBS": 304,
+ "INV_SALES": 18192.16,
+ "CUST_CODE": "2053968",
+ "SREP_CODE": "110",
+ "SREP_CODE_ALT": "056"
+ },
+ {
+ "JOBS": 40,
+ "INV_SALES": 3253.31,
+ "CUST_CODE": "4523057",
+ "SREP_CODE": "538",
+ "SREP_CODE_ALT": "006"
+ },
+ {
+ "JOBS": 332,
+ "INV_SALES": 18538.62,
+ "CUST_CODE": "8046621",
+ "SREP_CODE": "103",
+ "SREP_CODE_ALT": "029"
+ }
+];