Persisting the Grid State (React)

This example shows how you can save and restore the FlexGrid state, including columnLayout, sortDescriptions, and filter definitions. Try reordering, resizing, sorting, and filtering the columns on the grid below. Then press the "Save" button to save the state to local storage. Run the sample again and press the "Restore" button to restore the layout that you saved.

Learn about FlexGrid | FlexGrid API Reference

This example uses React.

import "bootstrap.css"; import "@mescius/wijmo.styles/wijmo.css"; import "./app.css"; import ReactDOM from "react-dom/client"; import React, { useRef, useState } from "react"; import { CellMaker } from "@mescius/wijmo.grid.cellmaker"; import { FlexGridFilter } from "@mescius/wijmo.react.grid.filter"; import { FlexGrid, FlexGridCellTemplate, FlexGridColumn, } from "@mescius/wijmo.react.grid"; import { DataMap } from "@mescius/wijmo.grid"; import { ComboBox } from "@mescius/wijmo.input"; import { CollectionView, SortDescription, } from "@mescius/wijmo"; import * as dataService from "./data"; const App = function (prop) { //columns definition const initialCols = [ { header: "ID", binding: "id" }, { header: "Country", binding: "country", width: 200, }, { header: "Products", binding: "products", width: 200, }, { header: "Downloads", binding: "downloads" }, { header: "Button CellMaker", binding: "button", width: 200 }, ]; const [cols, setCols] = useState(initialCols); const [data, setData] = useState(new CollectionView(dataService.data)); const gridRef = useRef(null); const filterRef = useRef(null); const initGrid = (grid) => { grid.draggedColumn.addHandler((s, e) => { setCols(JSON.parse(grid.columnLayout).columns); }); }; const hasTemplate = (binding) => { switch (binding) { case "downloads": return true; default: return false; } }; const getDataMap = (binding) => { const map = new DataMap(dataService.countries, "id", "name"); switch (binding) { case "country": return map; default: return undefined; } }; const getEditor = (binding) => { switch (binding) { case "products": return new ComboBox(document.createElement("div"), { itemsSource: dataService.products, selectedValuePath: "id", displayMemberPath: "name", }); default: return undefined; } }; const getTemp = (binding) => { switch (binding) { case "button": { return CellMaker.makeButton({ text: "<b>${item.products}</b> Button", click: (e, ctx) => { alert("Button clicked: " + ctx.item.id); }, }); } } }; const getTemplate = (binding, cellCtx) => { switch (binding) { case "downloads": { return (<React.Fragment> {cellCtx.item.downloads > 10000 ? (<span className="high-val">{cellCtx.item[binding]}</span>) : (<span className="low-val">{cellCtx.item[binding]}</span>)} </React.Fragment>); } } }; const saveLayout = () => { let grid = gridRef.current.control; let gridFilter = filterRef.current.control; var state = { columns: grid.columnLayout, filterDefinition: gridFilter.filterDefinition, sortDescriptions: grid.collectionView.sortDescriptions.map(function (sortDesc) { return { property: sortDesc.property, ascending: sortDesc.ascending }; }), }; localStorage["GridStateReact"] = JSON.stringify(state); }; const restoreLayout = () => { let grid = gridRef.current.control; let gridFilter = filterRef.current.control; let layoutDef = localStorage["GridStateReact"]; if (layoutDef) { var state = JSON.parse(layoutDef); //restore column layout let columnsArr = JSON.parse(state.columns).columns; setCols(columnsArr); const bindingMap = {}; columnsArr.forEach((item, index) => { bindingMap[item.binding] = index; }); grid.columns.sort((a, b) => { return bindingMap[a.binding] - bindingMap[b.binding]; }); // restore filter definitions setTimeout(() => { gridFilter.filterDefinition = state.filterDefinition; }); //delay to let columns render first properly // restore sort state var view = grid.collectionView; view.deferUpdate(function () { view.sortDescriptions.clear(); for (var i = 0; i < state.sortDescriptions.length; i++) { var sortDesc = state.sortDescriptions[i]; view.sortDescriptions.push(new SortDescription(sortDesc.property, sortDesc.ascending)); } }); } }; return (<main className="container-fluid"> <FlexGrid ref={gridRef} initialized={initGrid} itemsSource={data}> {cols.map((col) => (<FlexGridColumn key={col.binding} header={col.header} binding={col.binding} width={col.width} dataMap={getDataMap(col.binding)} editor={getEditor(col.binding)} cellTemplate={getTemp(col.binding)}> {hasTemplate(col.binding) ? (<FlexGridCellTemplate cellType="Cell" template={(cell) => getTemplate(col.binding, cell)}/>) : ("")} </FlexGridColumn>))} <FlexGridFilter ref={filterRef}/> </FlexGrid> <br /> <button className="btn btn-default" onClick={saveLayout}> Save State </button> <button className="btn btn-default" onClick={restoreLayout}> Restore State </button> </main>); }; const container = document.getElementById("app"); if (container) { const root = ReactDOM.createRoot(container); root.render(<App />); }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo Wijmo FlexGrid Persisting the Grid State</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.40/system.src.js" integrity="sha512-G6mEj6h18+m3MvzdviSDfPle/TfH0//cXcB33AKlNR/Rha0yQsKefDZKRTkIZos97HEGq2JMV1RT5ybMoQ3WsQ==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html>
.wj-flexgrid { max-height: 250px; margin: 10px 0; } .wj-flexgrid .wj-cell { padding: 6px 8px; } body { margin-bottom: 20px; } .high-val { color: darkgreen; font-weight: bold; } .low-val { color: darkred; font-weight: bold; }
// data used to generate random items function getData() { let countries = getCountries(), products = getProducts(), data = []; for (var i = 0; i < 30; i++) { data.push({ id: i, country: countries[i % countries.length].id, products: products[i % products.length].name, downloads: Math.round(Math.random() * 20000), button: '' }); } return data; } export const data = getData(); // function getCountries() { return [ { id: 0, name: "US" }, { id: 1, name: "Germany" }, { id: 2, name: "UK" }, { id: 3, name: "Japan" }, { id: 4, name: "Italy" }, { id: 5, name: "Greece" }, ]; } export const countries = getCountries(); function getProducts() { return [ { id: 0, name: 'Widgets' }, { id: 1, name: 'Gadgets' }, { id: 2, name: 'Yahoos' } ]; } export const products = getProducts();
(function (global) { window.process = { env: { NODE_ENV: "production" } } System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true, react: true }, meta: { '*.css': { loader: 'css' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { 'jszip': 'npm:jszip/dist/jszip.js', '@mescius/wijmo': 'npm:@mescius/wijmo/index.js', '@mescius/wijmo.input': 'npm:@mescius/wijmo.input/index.js', '@mescius/wijmo.styles': 'npm:@mescius/wijmo.styles', '@mescius/wijmo.cultures': 'npm:@mescius/wijmo.cultures', '@mescius/wijmo.chart': 'npm:@mescius/wijmo.chart/index.js', '@mescius/wijmo.chart.analytics': 'npm:@mescius/wijmo.chart.analytics/index.js', '@mescius/wijmo.chart.animation': 'npm:@mescius/wijmo.chart.animation/index.js', '@mescius/wijmo.chart.annotation': 'npm:@mescius/wijmo.chart.annotation/index.js', '@mescius/wijmo.chart.finance': 'npm:@mescius/wijmo.chart.finance/index.js', '@mescius/wijmo.chart.finance.analytics': 'npm:@mescius/wijmo.chart.finance.analytics/index.js', '@mescius/wijmo.chart.hierarchical': 'npm:@mescius/wijmo.chart.hierarchical/index.js', '@mescius/wijmo.chart.interaction': 'npm:@mescius/wijmo.chart.interaction/index.js', '@mescius/wijmo.chart.radar': 'npm:@mescius/wijmo.chart.radar/index.js', '@mescius/wijmo.chart.render': 'npm:@mescius/wijmo.chart.render/index.js', '@mescius/wijmo.chart.webgl': 'npm:@mescius/wijmo.chart.webgl/index.js', '@mescius/wijmo.chart.map': 'npm:@mescius/wijmo.chart.map/index.js', '@mescius/wijmo.gauge': 'npm:@mescius/wijmo.gauge/index.js', '@mescius/wijmo.grid': 'npm:@mescius/wijmo.grid/index.js', '@mescius/wijmo.grid.detail': 'npm:@mescius/wijmo.grid.detail/index.js', '@mescius/wijmo.grid.filter': 'npm:@mescius/wijmo.grid.filter/index.js', '@mescius/wijmo.grid.search': 'npm:@mescius/wijmo.grid.search/index.js', '@mescius/wijmo.grid.style': 'npm:@mescius/wijmo.grid.style/index.js', '@mescius/wijmo.grid.grouppanel': 'npm:@mescius/wijmo.grid.grouppanel/index.js', '@mescius/wijmo.grid.multirow': 'npm:@mescius/wijmo.grid.multirow/index.js', '@mescius/wijmo.grid.transposed': 'npm:@mescius/wijmo.grid.transposed/index.js', '@mescius/wijmo.grid.transposedmultirow': 'npm:@mescius/wijmo.grid.transposedmultirow/index.js', '@mescius/wijmo.grid.pdf': 'npm:@mescius/wijmo.grid.pdf/index.js', '@mescius/wijmo.grid.sheet': 'npm:@mescius/wijmo.grid.sheet/index.js', '@mescius/wijmo.grid.xlsx': 'npm:@mescius/wijmo.grid.xlsx/index.js', '@mescius/wijmo.grid.selector': 'npm:@mescius/wijmo.grid.selector/index.js', '@mescius/wijmo.grid.cellmaker': 'npm:@mescius/wijmo.grid.cellmaker/index.js', '@mescius/wijmo.grid.immutable': 'npm:@mescius/wijmo.grid.immutable/index.js', '@mescius/wijmo.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/index.js', '@mescius/wijmo.nav': 'npm:@mescius/wijmo.nav/index.js', '@mescius/wijmo.odata': 'npm:@mescius/wijmo.odata/index.js', '@mescius/wijmo.olap': 'npm:@mescius/wijmo.olap/index.js', '@mescius/wijmo.rest': 'npm:@mescius/wijmo.rest/index.js', '@mescius/wijmo.pdf': 'npm:@mescius/wijmo.pdf/index.js', '@mescius/wijmo.pdf.security': 'npm:@mescius/wijmo.pdf.security/index.js', '@mescius/wijmo.viewer': 'npm:@mescius/wijmo.viewer/index.js', '@mescius/wijmo.xlsx': 'npm:@mescius/wijmo.xlsx/index.js', '@mescius/wijmo.undo': 'npm:@mescius/wijmo.undo/index.js', '@mescius/wijmo.interop.grid': 'npm:@mescius/wijmo.interop.grid/index.js', '@mescius/wijmo.barcode': 'npm:@mescius/wijmo.barcode/index.js', '@mescius/wijmo.barcode.common': 'npm:@mescius/wijmo.barcode.common/index.js', '@mescius/wijmo.barcode.composite': 'npm:@mescius/wijmo.barcode.composite/index.js', '@mescius/wijmo.barcode.specialized': 'npm:@mescius/wijmo.barcode.specialized/index.js', "@mescius/wijmo.react.chart.analytics": "npm:@mescius/wijmo.react.chart.analytics/index.js", "@mescius/wijmo.react.chart.animation": "npm:@mescius/wijmo.react.chart.animation/index.js", "@mescius/wijmo.react.chart.annotation": "npm:@mescius/wijmo.react.chart.annotation/index.js", "@mescius/wijmo.react.chart.finance.analytics": "npm:@mescius/wijmo.react.chart.finance.analytics/index.js", "@mescius/wijmo.react.chart.finance": "npm:@mescius/wijmo.react.chart.finance/index.js", "@mescius/wijmo.react.chart.hierarchical": "npm:@mescius/wijmo.react.chart.hierarchical/index.js", "@mescius/wijmo.react.chart.interaction": "npm:@mescius/wijmo.react.chart.interaction/index.js", "@mescius/wijmo.react.chart.radar": "npm:@mescius/wijmo.react.chart.radar/index.js", "@mescius/wijmo.react.chart": "npm:@mescius/wijmo.react.chart/index.js", "@mescius/wijmo.react.core": "npm:@mescius/wijmo.react.core/index.js", '@mescius/wijmo.react.chart.map': 'npm:@mescius/wijmo.react.chart.map/index.js', "@mescius/wijmo.react.gauge": "npm:@mescius/wijmo.react.gauge/index.js", "@mescius/wijmo.react.grid.detail": "npm:@mescius/wijmo.react.grid.detail/index.js", "@mescius/wijmo.react.grid.filter": "npm:@mescius/wijmo.react.grid.filter/index.js", "@mescius/wijmo.react.grid.grouppanel": "npm:@mescius/wijmo.react.grid.grouppanel/index.js", '@mescius/wijmo.react.grid.search': 'npm:@mescius/wijmo.react.grid.search/index.js', "@mescius/wijmo.react.grid.multirow": "npm:@mescius/wijmo.react.grid.multirow/index.js", "@mescius/wijmo.react.grid.sheet": "npm:@mescius/wijmo.react.grid.sheet/index.js", '@mescius/wijmo.react.grid.transposed': 'npm:@mescius/wijmo.react.grid.transposed/index.js', '@mescius/wijmo.react.grid.transposedmultirow': 'npm:@mescius/wijmo.react.grid.transposedmultirow/index.js', '@mescius/wijmo.react.grid.immutable': 'npm:@mescius/wijmo.react.grid.immutable/index.js', "@mescius/wijmo.react.grid": "npm:@mescius/wijmo.react.grid/index.js", "@mescius/wijmo.react.input": "npm:@mescius/wijmo.react.input/index.js", "@mescius/wijmo.react.olap": "npm:@mescius/wijmo.react.olap/index.js", "@mescius/wijmo.react.viewer": "npm:@mescius/wijmo.react.viewer/index.js", "@mescius/wijmo.react.nav": "npm:@mescius/wijmo.react.nav/index.js", "@mescius/wijmo.react.base": "npm:@mescius/wijmo.react.base/index.js", '@mescius/wijmo.react.barcode.common': 'npm:@mescius/wijmo.react.barcode.common/index.js', '@mescius/wijmo.react.barcode.composite': 'npm:@mescius/wijmo.react.barcode.composite/index.js', '@mescius/wijmo.react.barcode.specialized': 'npm:@mescius/wijmo.react.barcode.specialized/index.js', 'jszip': 'npm:jszip/dist/jszip.js', 'react': 'npm:react/index.js', 'react-dom': 'npm:react-dom/index.js', 'react-dom/client': 'npm:react-dom/client.js', "scheduler": "npm:scheduler/index.js", 'redux': 'npm:redux/dist/redux.min.js', 'react-redux': 'npm:react-redux/dist/react-redux.min.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'css': 'npm:systemjs-plugin-css/css.js', 'plugin-babel': 'npm:systemjs-plugin-babel/plugin-babel.js', 'systemjs-babel-build':'npm:systemjs-plugin-babel/systemjs-babel-browser.js', "react-use-event-hook": "npm:react-use-event-hook/dist/esm/useEvent.js", }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);