Save and load report in React Pivot Table
10 Aug 202624 minutes to read
The Syncfusion® Pivot Table component allows users to save and restore report settings—also known as dataSourceSettings—through custom toolbar options. This functionality simplifies report management by allowing users to export settings as a JSON file and import them later to restore the Pivot Table configuration.
Customizing the Toolbar
To enable the export and import functionality, customize the Pivot Table’s toolbar using the toolbarTemplate property. Add Save and Open buttons as described below. The sample also requires the Toolbar service, which must be injected into the PivotViewComponent via <Inject services={[Toolbar]} />.
The template referenced by
toolbarTemplate(for example,#template) must be adivthat is a sibling of thePivotViewComponentin the DOM. The sample below places the<div id="template">immediately after the component.
Save Button
The Save button is rendered as an anchor element. When clicked, the current report settings are retrieved using the getPersistData method, converted into a base64-encoded JSON string, and assigned to the anchor’s href attribute so the browser downloads a file named pivot.JSON. The downloaded file contains the full dataSourceSettings, including rows, columns, values, and filters.
Open Button
The Open button is rendered as a label-wrapped file input. When a file is selected, the change event reads the file as text and assigns the parsed JSON to the Pivot Table’s dataSourceSettings, restoring the saved report.
Here is an example demonstrating how to add Save and Open buttons to the toolbar and handle exporting and importing report configurations in the Syncfusion® Pivot Table component:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { PivotViewComponent, Inject, Toolbar } from '@syncfusion/ej2-react-pivotview';
import { pivotData } from './datasource';
function App() {
let dataSourceSettings = {
columns: [{ name: 'Year', caption: 'Production Year' }, { name: 'Quarter' }],
dataSource: pivotData,
formatSettings: [{ name: 'Amount', format: 'C0' }],
rows: [{ name: 'Country' }, { name: 'Products' }],
values: [{ name: 'Sold', caption: 'Units Sold' }, { name: 'Amount', caption: 'Sold Amount' }]
};
let pivotObj;
const toolbarOptions = '#template';
function ondataBound() {
const dataSource = JSON.parse(pivotObj.getPersistData()).dataSourceSettings;
const a = document.getElementById('save');
const mime_type = 'application/octet-stream';
a.setAttribute('download', 'pivot.JSON');
a.href = 'data:' + mime_type + ';base64,' + btoa(JSON.stringify(dataSource) || '');
document.getElementById('files').addEventListener('change', readBlob, false);
}
function readBlob() {
const files = document.getElementById('files').files;
const file = files[0];
let start = 0, stop = 0;
if (file && file.size) {
stop = file.size - 1;
}
const reader = new FileReader();
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) {
pivotObj.dataSourceSettings = JSON.parse(evt.target.result);
}
};
if (file) {
const blob = file.slice(start, stop + 1);
reader.readAsBinaryString(blob);
}
document.getElementById('files').value = '';
}
return (
<div>
<PivotViewComponent
id='PivotView'
ref={d => pivotObj = d}
dataSourceSettings={dataSourceSettings}
width={'100%'}
height={350}
showToolbar={true}
toolbarTemplate={toolbarOptions}
dataBound={ondataBound.bind(this)}
>
<Inject services={[Toolbar]} />
</PivotViewComponent>
<div id='template'>
<label htmlFor="files" className="e-btn-icon e-open-report e-icons icon-container">
<input
id="files"
type="file"
className="file-input"
onChange={readBlob}
/>
<span className="icon-text">Open</span>
</label>
<a id="save" className="e-btn-icon e-save-report e-icons icon-container">
<span className="icon-text">Save</span>
</a>
</div>
</div>
);
}
;
export default App;
ReactDOM.render(<App />, document.getElementById('root'));import * as React from 'react';
import * as ReactDOM from 'react-dom';
import {
PivotViewComponent, Inject, Toolbar, IDataSet
} from '@syncfusion/ej2-react-pivotview';
import { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import { pivotData } from './datasource';
function App() {
let dataSourceSettings: DataSourceSettingsModel = {
columns: [{ name: 'Year', caption: 'Production Year' }, { name: 'Quarter' }],
dataSource: pivotData as IDataSet[],
formatSettings: [{ name: 'Amount', format: 'C0' }],
rows: [{ name: 'Country' }, { name: 'Products' }],
values: [{ name: 'Sold', caption: 'Units Sold' }, { name: 'Amount', caption: 'Sold Amount' }]
};
let pivotObj: PivotViewComponent;
const toolbarOptions: any = '#template';
function ondataBound(): void {
const dataSource = JSON.parse(pivotObj.getPersistData()).dataSourceSettings;
const a: HTMLAnchorElement = document.getElementById('save') as HTMLAnchorElement;
const mime_type = 'application/octet-stream'; // text/html, image/png, et c
a.setAttribute('download', 'pivot.JSON');
a.href = 'data:' + mime_type + ';base64,' + btoa(JSON.stringify(dataSource) || '');
(document.getElementById('files') as HTMLInputElement).addEventListener('change', readBlob, false);
}
function readBlob(): void {
const files: FileList = (document.getElementById('files') as HTMLInputElement).files as FileList;
const file: File = files[0];
let start: number = 0, stop: number = 0;
if (file && file.size) {
stop = file.size - 1;
}
const reader: FileReader = new FileReader();
reader.onloadend = function (evt: ProgressEvent<FileReader>) {
if ((evt.target as FileReader).readyState == FileReader.DONE) {
pivotObj.dataSourceSettings = JSON.parse((evt.target as FileReader).result as string);
}
};
if (file) {
const blob: Blob = file.slice(start, stop + 1);
reader.readAsBinaryString(blob);
}
(document.getElementById('files') as HTMLInputElement).value = '';
}
return (
<div>
<PivotViewComponent
id='PivotView'
ref={d => pivotObj = d}
dataSourceSettings={dataSourceSettings}
width={'100%'}
height={350}
showToolbar={true}
toolbarTemplate={toolbarOptions}
dataBound={ondataBound.bind(this)}
>
<Inject services={[Toolbar]} />
</PivotViewComponent>
<div id='template'>
<label htmlFor="files" className="e-btn-icon e-open-report e-icons icon-container">
<input
id="files"
type="file"
className="file-input"
onChange={readBlob}
/>
<span className="icon-text">Open</span>
</label>
<a id="save" className="e-btn-icon e-save-report e-icons icon-container">
<span className="icon-text">Save</span>
</a>
</div>
</div>
);
}
export default App;
ReactDOM.render(<App />, document.getElementById('root'));export let pivotData = [
{ 'In_Stock': 34, 'Sold': 51, 'Amount': 383, 'Country': 'France', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 4, 'Sold': 423, 'Amount': 3595.5, 'Country': 'France', 'Product_Categories': 'Accessories', 'Products': 'Cleaners', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 11, 'Sold': 19, 'Amount': 85.5, 'Country': 'France', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2017', 'Quarter': 'Q4' },
{ 'In_Stock': 10, 'Sold': 64, 'Amount': 320, 'Country': 'France', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Sales Person', 'Year': 'FY 2018', 'Quarter': 'Q4' },
{ 'In_Stock': 2, 'Sold': 141, 'Amount': 1692, 'Country': 'France', 'Product_Categories': 'Clothing', 'Products': 'Jerseys', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 30, 'Sold': 332, 'Amount': 3735, 'Country': 'France', 'Product_Categories': 'Clothing', 'Products': 'Shorts', 'Order_Source': 'Teleshopping', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 9, 'Sold': 353, 'Amount': 3000.5, 'Country': 'Germany', 'Product_Categories': 'Clothing', 'Products': 'Vests', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 32, 'Sold': 269, 'Amount': 1345, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Helmets', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 31, 'Sold': 73, 'Amount': 1387, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Tires and Tubes', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 19, 'Sold': 279, 'Amount': 205363, 'Country': 'Germany', 'Product_Categories': 'Bikes', 'Products': 'Road Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 41, 'Sold': 82, 'Amount': 922.5, 'Country': 'Germany', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 15, 'Sold': 188, 'Amount': 1457, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Teleshopping', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 31, 'Sold': 78, 'Amount': 1677, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2015', 'Quarter': 'Q3' },
{ 'In_Stock': 46, 'Sold': 393, 'Amount': 6681, 'Country': 'United Kingdom', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q3' },
{ 'In_Stock': 35, 'Sold': 61, 'Amount': 991.25, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Fenders', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 34, 'Sold': 271, 'Amount': 4336, 'Country': 'United Kingdom', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 48, 'Sold': 361, 'Amount': 10469, 'Country': 'United Kingdom', 'Product_Categories': 'Clothing', 'Products': 'Shorts', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 20, 'Sold': 464, 'Amount': 13108, 'Country': 'United Kingdom', 'Product_Categories': 'Clothing', 'Products': 'Jerseys', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 45, 'Sold': 257, 'Amount': 28784, 'Country': 'United States', 'Product_Categories': 'Clothing', 'Products': 'Vests', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q4' },
{ 'In_Stock': 5, 'Sold': 333, 'Amount': 2081.25, 'Country': 'United States', 'Product_Categories': 'Clothing', 'Products': 'Gloves', 'Order_Source': 'Teleshopping', 'Year': 'FY 2016', 'Quarter': 'Q4' },
{ 'In_Stock': 47, 'Sold': 252, 'Amount': 401940, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Helmets', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 38, 'Sold': 287, 'Amount': 457765, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Fenders', 'Order_Source': 'App Store', 'Year': 'FY 2016', 'Quarter': 'Q3' },
{ 'In_Stock': 29, 'Sold': 92, 'Amount': 146786, 'Country': 'United States', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2018', 'Quarter': 'Q3' },
{ 'In_Stock': 14, 'Sold': 535, 'Amount': 10165, 'Country': 'United States', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q4' },
{ 'In_Stock': 47, 'Sold': 405, 'Amount': 3037.5, 'Country': 'United States', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q4' },
];export let pivotData: object[] = [
{ 'In_Stock': 34, 'Sold': 51, 'Amount': 383, 'Country': 'France', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 4, 'Sold': 423, 'Amount': 3595.5, 'Country': 'France', 'Product_Categories': 'Accessories', 'Products': 'Cleaners', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 11, 'Sold': 19, 'Amount': 85.5, 'Country': 'France', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2017', 'Quarter': 'Q4' },
{ 'In_Stock': 10, 'Sold': 64, 'Amount': 320, 'Country': 'France', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Sales Person', 'Year': 'FY 2018', 'Quarter': 'Q4' },
{ 'In_Stock': 2, 'Sold': 141, 'Amount': 1692, 'Country': 'France', 'Product_Categories': 'Clothing', 'Products': 'Jerseys', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 30, 'Sold': 332, 'Amount': 3735, 'Country': 'France', 'Product_Categories': 'Clothing', 'Products': 'Shorts', 'Order_Source': 'Teleshopping', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 9, 'Sold': 353, 'Amount': 3000.5, 'Country': 'Germany', 'Product_Categories': 'Clothing', 'Products': 'Vests', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 32, 'Sold': 269, 'Amount': 1345, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Helmets', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q1' },
{ 'In_Stock': 31, 'Sold': 73, 'Amount': 1387, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Tires and Tubes', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 19, 'Sold': 279, 'Amount': 205363, 'Country': 'Germany', 'Product_Categories': 'Bikes', 'Products': 'Road Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 41, 'Sold': 82, 'Amount': 922.5, 'Country': 'Germany', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 15, 'Sold': 188, 'Amount': 1457, 'Country': 'Germany', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Teleshopping', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 31, 'Sold': 78, 'Amount': 1677, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2015', 'Quarter': 'Q3' },
{ 'In_Stock': 46, 'Sold': 393, 'Amount': 6681, 'Country': 'United Kingdom', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'Sales Person', 'Year': 'FY 2016', 'Quarter': 'Q3' },
{ 'In_Stock': 35, 'Sold': 61, 'Amount': 991.25, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Fenders', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 34, 'Sold': 271, 'Amount': 4336, 'Country': 'United Kingdom', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 48, 'Sold': 361, 'Amount': 10469, 'Country': 'United Kingdom', 'Product_Categories': 'Clothing', 'Products': 'Shorts', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q1' },
{ 'In_Stock': 20, 'Sold': 464, 'Amount': 13108, 'Country': 'United Kingdom', 'Product_Categories': 'Clothing', 'Products': 'Jerseys', 'Order_Source': 'Teleshopping', 'Year': 'FY 2017', 'Quarter': 'Q1' },
{ 'In_Stock': 45, 'Sold': 257, 'Amount': 28784, 'Country': 'United States', 'Product_Categories': 'Clothing', 'Products': 'Vests', 'Order_Source': 'Sales Person', 'Year': 'FY 2015', 'Quarter': 'Q4' },
{ 'In_Stock': 5, 'Sold': 333, 'Amount': 2081.25, 'Country': 'United States', 'Product_Categories': 'Clothing', 'Products': 'Gloves', 'Order_Source': 'Teleshopping', 'Year': 'FY 2016', 'Quarter': 'Q4' },
{ 'In_Stock': 47, 'Sold': 252, 'Amount': 401940, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Helmets', 'Order_Source': 'App Store', 'Year': 'FY 2018', 'Quarter': 'Q1' },
{ 'In_Stock': 38, 'Sold': 287, 'Amount': 457765, 'Country': 'United Kingdom', 'Product_Categories': 'Accessories', 'Products': 'Fenders', 'Order_Source': 'App Store', 'Year': 'FY 2016', 'Quarter': 'Q3' },
{ 'In_Stock': 29, 'Sold': 92, 'Amount': 146786, 'Country': 'United States', 'Product_Categories': 'Bikes', 'Products': 'Touring Bikes', 'Order_Source': 'Retail Outlets', 'Year': 'FY 2018', 'Quarter': 'Q3' },
{ 'In_Stock': 14, 'Sold': 535, 'Amount': 10165, 'Country': 'United States', 'Product_Categories': 'Bikes', 'Products': 'Mountain Bikes', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q4' },
{ 'In_Stock': 47, 'Sold': 405, 'Amount': 3037.5, 'Country': 'United States', 'Product_Categories': 'Accessories', 'Products': 'Bottles and Cages', 'Order_Source': 'App Store', 'Year': 'FY 2017', 'Quarter': 'Q4' },
];For more information and to access the quick start project, visit: GitHub Repository