Skip to content

Commit 44f1ec7

Browse files
authored
fix(export): add checks for missing data - master (#16722)
* fix(export): add checks for missing data * test(exporter): empty data and summaries should not throw an error
1 parent a967f08 commit 44f1ec7

File tree

5 files changed

+134
-28
lines changed

5 files changed

+134
-28
lines changed

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

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ import { IgxHierarchicalGridExportComponent,
3434
IgxHierarchicalGridMCHCollapsibleComponent,
3535
IgxHierarchicalGridMultiColumnHeaderIslandsExportComponent,
3636
IgxHierarchicalGridMultiColumnHeadersExportComponent,
37-
IgxHierarchicalGridSummariesExportComponent
37+
IgxHierarchicalGridSummariesExportComponent,
38+
IgxHierarchicalGridEmptyDataExportComponent,
39+
IgxHierarchicalGridMissingChildDataExportComponent
3840
} from '../../../../../test-utils/hierarchical-grid-components.spec';
3941
import { GridFunctions } from '../../../../../test-utils/grid-functions.spec';
4042
import { IgxPivotGridMultipleRowComponent, IgxPivotGridTestComplexHierarchyComponent, SALES_DATA } from '../../../../../test-utils/pivot-grid-samples.spec';
@@ -75,6 +77,8 @@ describe('Excel Exporter', () => {
7577
IgxPivotGridTestComplexHierarchyComponent,
7678
IgxTreeGridSummariesKeyComponent,
7779
IgxHierarchicalGridSummariesExportComponent,
80+
IgxHierarchicalGridEmptyDataExportComponent,
81+
IgxHierarchicalGridMissingChildDataExportComponent,
7882
GroupedGridWithSummariesComponent,
7983
GridCurrencySummariesComponent,
8084
GridUserMeetingDataComponent,
@@ -931,6 +935,41 @@ describe('Excel Exporter', () => {
931935

932936
await exportAndVerify(hGrid, options, actualData.exportHierarchicalDataWithSkippedRows);
933937
});
938+
939+
it('should export hierarchical grid with empty data without throwing error', async () => {
940+
fix = TestBed.createComponent(IgxHierarchicalGridEmptyDataExportComponent);
941+
fix.detectChanges();
942+
943+
hGrid = fix.componentInstance.hGrid;
944+
options = createExportOptions('HierarchicalGridEmptyDataExcelExport');
945+
946+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
947+
});
948+
949+
it('should export hierarchical grid with empty data and summaries without throwing error', async () => {
950+
fix = TestBed.createComponent(IgxHierarchicalGridEmptyDataExportComponent);
951+
fix.detectChanges();
952+
953+
hGrid = fix.componentInstance.hGrid;
954+
const artistColumn = hGrid.columns.find(col => col.field === 'Artist');
955+
artistColumn.hasSummary = true;
956+
fix.detectChanges();
957+
958+
options = createExportOptions('HierarchicalGridEmptyDataWithSummariesExcelExport');
959+
options.exportSummaries = true;
960+
961+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
962+
});
963+
964+
it('should export hierarchical grid with missing child data key without throwing error', async () => {
965+
fix = TestBed.createComponent(IgxHierarchicalGridMissingChildDataExportComponent);
966+
fix.detectChanges();
967+
968+
hGrid = fix.componentInstance.hGrid;
969+
options = createExportOptions('HierarchicalGridMissingChildDataExcelExport');
970+
971+
await expectAsync(getExportedData(hGrid, options)).toBeResolved();
972+
});
934973
});
935974

936975
describe('', () => {

projects/igniteui-angular/grids/core/src/services/excel/excel-exporter.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export class IgxExcelExporterService extends IgxBaseExporter {
7777
const firstDataElement = data[0];
7878
const isHierarchicalGrid = firstDataElement?.type === ExportRecordType.HierarchicalGridRecord;
7979
const isPivotGrid = firstDataElement?.type === ExportRecordType.PivotGridRecord;
80+
const ownersKeys = Array.from(this._ownersMap.keys());
81+
const firstKey = ownersKeys[0];
82+
const isHierarchicalGridByMap = firstKey && typeof firstKey !== 'string';
83+
const filterColumns = (columns) => columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader);
8084

8185
let rootKeys;
8286
let columnCount;
@@ -113,20 +117,30 @@ export class IgxExcelExporterService extends IgxBaseExporter {
113117
rootKeys = this._ownersMap.get(firstDataElement.owner).columns.filter(c => !c.skip).map(c => c.field);
114118
defaultOwner = this._ownersMap.get(firstDataElement.owner);
115119
} else {
116-
defaultOwner = this._ownersMap.get(DEFAULT_OWNER);
117-
const columns = defaultOwner.columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader);
118-
119-
columnWidths = defaultOwner.columnWidths;
120-
indexOfLastPinnedColumn = defaultOwner.indexOfLastPinnedColumn;
121-
columnCount = isPivotGrid ? columns.length + this.pivotGridFilterFieldsCount : columns.length;
122-
rootKeys = columns.map(c => c.field);
120+
// Check if this is actually a hierarchical grid (when data only contains summary records)
121+
defaultOwner = isHierarchicalGridByMap
122+
? this._ownersMap.get(firstKey)
123+
: this._ownersMap.get(DEFAULT_OWNER) || this._ownersMap.get(firstKey);
124+
125+
if (defaultOwner) {
126+
const columns = filterColumns(defaultOwner.columns);
127+
128+
columnWidths = defaultOwner.columnWidths;
129+
indexOfLastPinnedColumn = defaultOwner.indexOfLastPinnedColumn;
130+
columnCount = isPivotGrid ? columns.length + this.pivotGridFilterFieldsCount : columns.length;
131+
rootKeys = columns.map(c => c.field);
132+
}
123133
}
124134
} else {
125-
const ownersKeys = Array.from(this._ownersMap.keys());
135+
// For hierarchical grids with empty data, use the grid instance; otherwise try DEFAULT_OWNER first
136+
defaultOwner = isHierarchicalGridByMap
137+
? this._ownersMap.get(firstKey)
138+
: this._ownersMap.get(DEFAULT_OWNER) || this._ownersMap.get(firstKey);
126139

127-
defaultOwner = this._ownersMap.get(ownersKeys[0]);
128-
columnWidths = defaultOwner.columnWidths;
129-
columnCount = defaultOwner.columns.filter(col => col.field !== GRID_LEVEL_COL && !col.skip && col.headerType === ExportHeaderType.ColumnHeader).length;
140+
if (defaultOwner) {
141+
columnWidths = defaultOwner.columnWidths;
142+
columnCount = filterColumns(defaultOwner.columns).length;
143+
}
130144
}
131145

132146
const worksheetData =

projects/igniteui-angular/grids/core/src/services/excel/excel-files.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,9 +551,15 @@ export class WorksheetFile implements IExcelFile {
551551
}
552552

553553
private getSummaryFunction(type: string, key: string, dimensionMapKey: any, recordLevel: number, col: IColumnInfo): string {
554-
const dimensionMap = dimensionMapKey ? this.hierarchicalDimensionMap.get(dimensionMapKey) : this.dimensionMap;
554+
const dimensionMap = dimensionMapKey ? (this.hierarchicalDimensionMap.get(dimensionMapKey) ?? this.dimensionMap) : this.dimensionMap;
555+
if (!dimensionMap) {
556+
return '';
557+
}
555558
const dimensions = dimensionMap.get(key);
556559
const levelDimensions = dimensionMap.get(GRID_LEVEL_COL);
560+
if (!dimensions || !levelDimensions) {
561+
return '';
562+
}
557563

558564
let func = '';
559565
let funcType = '';

projects/igniteui-angular/grids/core/src/services/exporter-common/base-export-service.ts

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ export abstract class IgxBaseExporter {
247247
const childLayoutList = grid.childLayoutList;
248248

249249
for (const island of childLayoutList) {
250-
this.mapHierarchicalGridColumns(island, grid.data[0]);
250+
const gridData = grid.data && grid.data.length > 0 ? grid.data[0] : {};
251+
this.mapHierarchicalGridColumns(island, gridData);
251252
}
252253
} else if (grid.type === 'pivot') {
253254
this.pivotGridColumns = [];
@@ -1212,32 +1213,37 @@ export abstract class IgxBaseExporter {
12121213
let keyData;
12131214

12141215
if (island.autoGenerate) {
1215-
keyData = gridData[island.key];
1216-
const islandKeys = island.children.map(i => i.key);
1216+
keyData = gridData && gridData[island.key] ? gridData[island.key] : undefined;
1217+
const islandKeys = island.children && island.children.length > 0 ? island.children.map(i => i.key) : [];
12171218

1218-
const islandData = keyData.map(i => {
1219-
const newItem = {};
1219+
if (keyData && Array.isArray(keyData) && keyData.length > 0) {
1220+
const islandData = keyData.map(i => {
1221+
const newItem = {};
12201222

1221-
Object.keys(i).map(k => {
1222-
if (!islandKeys.includes(k)) {
1223-
newItem[k] = i[k];
1224-
}
1225-
});
1223+
Object.keys(i).map(k => {
1224+
if (!islandKeys.includes(k)) {
1225+
newItem[k] = i[k];
1226+
}
1227+
});
12261228

1227-
return newItem;
1228-
});
1229+
return newItem;
1230+
});
12291231

1230-
columnList = this.getAutoGeneratedColumns(islandData);
1232+
columnList = this.getAutoGeneratedColumns(islandData);
1233+
} else {
1234+
// If no data available, create empty column list
1235+
columnList = this.getAutoGeneratedColumns([{}]);
1236+
}
12311237
} else {
12321238
const islandColumnList = island.columns;
12331239
columnList = this.getColumns(islandColumnList);
12341240
}
12351241

12361242
this._ownersMap.set(island, columnList);
12371243

1238-
if (island.children.length > 0) {
1244+
if (island.children && island.children.length > 0) {
12391245
for (const childIsland of island.children) {
1240-
const islandKeyData = keyData !== undefined ? keyData[0] : {};
1246+
const islandKeyData = keyData && Array.isArray(keyData) && keyData.length > 0 ? keyData[0] : {};
12411247
this.mapHierarchicalGridColumns(childIsland, islandKeyData);
12421248
}
12431249
}

projects/igniteui-angular/test-utils/hierarchical-grid-components.spec.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,3 +748,44 @@ export class IgxHierarchicalGridDefaultComponent {
748748
this.data = SampleTestData.hierarchicalGridSingersFullData();
749749
}
750750
}
751+
752+
@Component({
753+
template: `
754+
<igx-hierarchical-grid [data]="data" [height]="'1200px'" [width]="'700px'" #hierarchicalGrid>
755+
<igx-column field="Artist" [sortable]="true"></igx-column>
756+
<igx-column field="Debut" [sortable]="true" dataType="number"></igx-column>
757+
<igx-column field="GrammyNominations" header="Grammy Nominations" [sortable]="true"></igx-column>
758+
<igx-column field="GrammyAwards" header="Grammy Awards" [sortable]="true"></igx-column>
759+
760+
<igx-row-island [key]="'Albums'" [autoGenerate]="true">
761+
</igx-row-island>
762+
</igx-hierarchical-grid>
763+
`,
764+
imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent]
765+
})
766+
export class IgxHierarchicalGridEmptyDataExportComponent {
767+
@ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hGrid: IgxHierarchicalGridComponent;
768+
public data = [];
769+
}
770+
771+
@Component({
772+
template: `
773+
<igx-hierarchical-grid [data]="data" [height]="'1200px'" [width]="'700px'" #hierarchicalGrid>
774+
<igx-column field="Artist" [sortable]="true"></igx-column>
775+
<igx-column field="Debut" [sortable]="true" dataType="number"></igx-column>
776+
<igx-column field="GrammyNominations" header="Grammy Nominations" [sortable]="true"></igx-column>
777+
<igx-column field="GrammyAwards" header="Grammy Awards" [sortable]="true"></igx-column>
778+
779+
<igx-row-island [key]="'Albums'" [autoGenerate]="true">
780+
</igx-row-island>
781+
</igx-hierarchical-grid>
782+
`,
783+
imports: [IgxHierarchicalGridComponent, IgxColumnComponent, IgxRowIslandComponent]
784+
})
785+
export class IgxHierarchicalGridMissingChildDataExportComponent {
786+
@ViewChild('hierarchicalGrid', { read: IgxHierarchicalGridComponent, static: true }) public hGrid: IgxHierarchicalGridComponent;
787+
// Data without the 'Albums' key that the row island expects
788+
public data = [
789+
{ Artist: 'Artist1', Debut: 2000, GrammyNominations: 5, GrammyAwards: 2 }
790+
];
791+
}

0 commit comments

Comments
 (0)