Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export class CharSeparatedValueData {
this._escapeCharacters.push(this._delimiter);

const headers = columns && columns.length ?
columns.map(c => c.header ?? c.field) :
/* When column groups are present, always use the field as it indicates the group the column belongs to.
* Otherwise, in PivotGrid scenarios we can end up with many duplicated column names without a hint what they represent.
*/
columns.map(c => c.columnGroupParent ? c.field : c.header ?? c.field) :
keys;

this._headerRecord = this.processHeaderRecord(headers, this._data.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import { FilteringLogic } from '../../data-operations/filtering-expression.inter
import { configureTestSuite } from '../../test-utils/configure-suite';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { wait } from '../../test-utils/ui-interactions.spec';
import { IgxPivotGridComponent } from '../../grids/pivot-grid/pivot-grid.component';
import { IgxPivotGridTestBaseComponent } from '../../test-utils/pivot-grid-samples.spec';
import { IgxPivotNumericAggregate } from '../../grids/pivot-grid/pivot-grid-aggregate';

describe('CSV Grid Exporter', () => {
configureTestSuite();
Expand Down Expand Up @@ -513,6 +516,48 @@ describe('CSV Grid Exporter', () => {
});
});

describe('Pivot Grid CSV export', () => {
let fix;
let pivotGrid: IgxPivotGridComponent;
beforeEach(() => {
fix = TestBed.createComponent(IgxPivotGridTestBaseComponent);
fix.detectChanges();
pivotGrid = fix.componentInstance.pivotGrid;
pivotGrid.pivotConfiguration = {
columns: [
{
enabled: true,
memberName: 'Country'
}
],
rows: [
{
enabled: true,
memberName: 'ProductCategory'
}
],
values: [
{
enabled: true,
member: 'UnitsSold',
aggregate: {
aggregator: IgxPivotNumericAggregate.sum,
key: 'SUM',
label: 'Sum',
},
}
]
};
fix.detectChanges();
});

it('should export pivot grid successfully.', async () => {
await wait();
const wrapper = await getExportedData(pivotGrid, options);
wrapper.verifyData(wrapper.pivotGridData);
});
});

const getExportedData = (grid, csvOptions: IgxCsvExporterOptions) => {
const result = new Promise<CSVWrapper>((resolve) => {
exporter.exportEnded.pipe(first()).subscribe((value) => {
Expand Down
25 changes: 22 additions & 3 deletions projects/igniteui-angular/src/lib/services/csv/csv-exporter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { EventEmitter, Injectable } from '@angular/core';
import { DEFAULT_OWNER, IExportRecord, IgxBaseExporter } from '../exporter-common/base-export-service';
import { DEFAULT_OWNER, ExportHeaderType, IColumnInfo, IExportRecord, IgxBaseExporter } from '../exporter-common/base-export-service';
import { ExportUtilities } from '../exporter-common/export-utilities';
import { CharSeparatedValueData } from './char-separated-value-data';
import { CsvFileTypes, IgxCsvExporterOptions } from './csv-exporter-options';
Expand Down Expand Up @@ -50,10 +50,29 @@ export class IgxCsvExporterService extends IgxBaseExporter {
private _stringData: string;

protected exportDataImplementation(data: IExportRecord[], options: IgxCsvExporterOptions, done: () => void) {
data = data.map((item) => item.data);
const dimensionKeys = data[0]?.dimensionKeys;
data = dimensionKeys?.length ?
data.map((item) => item.rawData):
data.map((item) => item.data);
const columnList = this._ownersMap.get(DEFAULT_OWNER);
const columns = columnList?.columns.filter(c => c.headerType === ExportHeaderType.ColumnHeader);
if (dimensionKeys) {
const dimensionCols = dimensionKeys.map((key) => {
const columnInfo: IColumnInfo = {
header: key,
field: key,
dataType: 'string',
skip: false,
headerType: ExportHeaderType.ColumnHeader,
columnSpan: 1,
startIndex: 0
};
return columnInfo;
});
columns.unshift(...dimensionCols);
}

const csvData = new CharSeparatedValueData(data, options.valueDelimiter, columnList?.columns);
const csvData = new CharSeparatedValueData(data, options.valueDelimiter, columns);
csvData.prepareDataAsync((r) => {
this._stringData = r;
this.saveFile(options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,4 +288,12 @@ export class CSVWrapper {
`B's Beverages${this._delimiter}Victoria Ashworth${this._delimiter}Fauntleroy Circus${this._delimiter}0${this._delimiter}` +
`2500${this._delimiter}5000${this._eor}`;
}

public get pivotGridData() {
return `ProductCategory${this._delimiter}Bulgaria${this._delimiter}USA${this._delimiter}Uruguay${this._eor}` +
`Accessories${this._delimiter}${this._delimiter}293${this._delimiter}${this._eor}` +
`Bikes${this._delimiter}${this._delimiter}${this._delimiter}68${this._eor}` +
`Clothing${this._delimiter}774${this._delimiter}296${this._delimiter}456${this._eor}` +
`Components${this._delimiter}${this._delimiter}240${this._delimiter}${this._eor}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ export interface IExportRecord {
summaryKey?: string;
hierarchicalOwner?: string;
references?: IColumnInfo[];
/* Adding `rawData` and `dimesnionKeys` properties to support properly exporting pivot grid data to CSV. */
rawData?: any;
dimensionKeys?: string[];
}

export interface IColumnList {
Expand Down Expand Up @@ -448,13 +451,18 @@ export abstract class IgxBaseExporter {
if (!isSpecialData) {
const owner = record.owner === undefined ? DEFAULT_OWNER : record.owner;
const ownerCols = this._ownersMap.get(owner).columns;
const hasRowHeaders = ownerCols.some(c => c.headerType === ExportHeaderType.RowHeader);

if (record.type !== ExportRecordType.HeaderRecord) {
const columns = ownerCols
.filter(c => c.headerType === ExportHeaderType.ColumnHeader && !c.skip)
.sort((a, b) => a.startIndex - b.startIndex)
.sort((a, b) => a.pinnedIndex - b.pinnedIndex);

if (hasRowHeaders) {
record.rawData = record.data;
}

record.data = columns.reduce((a, e) => {
if (!e.skip) {
let rawValue = resolveNestedPath(record.data, e.field);
Expand Down Expand Up @@ -592,6 +600,10 @@ export abstract class IgxBaseExporter {

this.flatRecords.push(pivotGridRecord);
}

if (this.flatRecords.length) {
this.flatRecords[0].dimensionKeys = Object.values(this.pivotGridRowDimensionsMap);
}
}

private prepareHierarchicalGridData(grid: GridType, hasFiltering: boolean, hasSorting: boolean) {
Expand Down Expand Up @@ -1342,8 +1354,8 @@ export abstract class IgxBaseExporter {

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)));

&& mapValues.every(mv => Object.values(row).includes(mv)));
if (groupedRecords[k].length === 0) {
delete groupedRecords[k];
}
Expand Down
Loading