Persisting the Grid State (Angular)

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 Angular.

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; // import '@angular/compiler'; import { Component, enableProdMode, ViewChild, OnInit, ɵresolveComponentResources } from '@angular/core'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { WjGridModule } from '@mescius/wijmo.angular2.grid'; import { WjGridFilterModule } from '@mescius/wijmo.angular2.grid.filter'; import { WjInputModule } from '@mescius/wijmo.angular2.input'; import { CellMaker } from '@mescius/wijmo.grid.cellmaker'; import { ComboBox } from '@mescius/wijmo.input'; import { SortDescription } from '@mescius/wijmo'; import { FlexGrid, DataMap } from '@mescius/wijmo.grid'; import { FlexGridFilter } from '@mescius/wijmo.grid.filter'; import * as dataService from './app.data'; // @Component({ standalone: true, providers: [], imports: [WjGridModule, BrowserModule, WjGridModule, WjInputModule, WjGridFilterModule], selector: 'app-component', templateUrl: 'src/app.component.html', }) export class AppComponent implements OnInit { //columns definition 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 }, ]; dataMaps: { [key: string]: DataMap } = {}; editors: { [key: string]: ComboBox } = {}; templates: { [key: string]: any } = {}; tempButtons: { [key: string]: any } = {}; @ViewChild('flexGrid', { static: true }) flexGrid: FlexGrid; @ViewChild('filter', { static: true }) filter: FlexGridFilter; data: any; // DataSvc will be passed by derived classes constructor() { this.data = dataService.getData(); } ngOnInit(): void { this.initializeDataMaps(); this.initializeEditors(); this.initializeTemplates(); } private initializeDataMaps(): void { const map = new DataMap(dataService.getCountries(), 'id', 'name'); this.dataMaps['country'] = map; // Add other data maps if needed } private initializeEditors(): void { const comboBox = new ComboBox(document.createElement('div'), { itemsSource: dataService.getProducts(), selectedValuePath: 'id', displayMemberPath: 'name', }); this.editors['products'] = comboBox; // Add other editors if needed } private initializeTemplates(): void { this.tempButtons['button'] = CellMaker.makeButton({ text: '<b>${item.products}</b> Button', click: (e: Event, ctx: any) => { alert('Button clicked: ' + ctx.item.id); }, }); // Add other templates if needed } hasTemplate(binding: string): boolean { return binding === 'downloads'; } getDataMap(binding: string): DataMap | undefined { return this.dataMaps[binding]; } getEditor(binding: string): ComboBox | undefined { return this.editors[binding]; } getTemp(binding: string): any { return this.tempButtons[binding]; } getTemplate(binding: string, cellCtx: any): any { if (binding === 'downloads') { return cellCtx.item.downloads > 10000 ? { html: `<span class="high-val">${cellCtx.item[binding]}</span>` } : { html: `<span class="low-val">${cellCtx.item[binding]}</span>` }; } return undefined; } // saveLayout() { var state = { columns: this.flexGrid.columnLayout, filterDefinition: this.filter.filterDefinition, sortDescriptions: this.flexGrid.collectionView.sortDescriptions.map(function (sortDesc) { return { property: sortDesc.property, ascending: sortDesc.ascending }; }), }; localStorage['GridStateAngular'] = JSON.stringify(state); } // restoreLayout() { var json = localStorage['GridStateAngular']; if (json) { var state = JSON.parse(json); // // restore column layout let columnsArr = JSON.parse(state.columns).columns; this.initialCols = columnsArr; // restore filter definitions setTimeout(() => { this.filter.filterDefinition = state.filterDefinition; }); //delay to let columns render first properly // restore sort state var view = this.flexGrid.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)); } }); } } } // enableProdMode(); // Resolve resources (templateUrl, styleUrls etc), After resolution all URLs have been converted into `template` strings. ɵresolveComponentResources(fetch).then(() => { // Bootstrap application bootstrapApplication(AppComponent).catch((err) => console.error(err)); });
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>MESCIUS Wijmo FlexGrid Persisting the Grid State</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Polyfills --> <script src="node_modules/core-js/client/shim.min.js"></script> <script src="node_modules/zone.js/fesm2015/zone.min.js"></script> <!-- SystemJS --> <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.21.5/system.src.js" integrity="sha512-skZbMyvYdNoZfLmiGn5ii6KmklM82rYX2uWctBhzaXPxJgiv4XBwJnFGr5k8s+6tE1pcR1nuTKghozJHyzMcoA==" crossorigin="anonymous"></script> <script src="systemjs.config.js"></script> <script> // workaround to load 'rxjs/operators' from the rxjs bundle System.import('rxjs').then(function (m) { System.set(SystemJS.resolveSync('rxjs/operators'), System.newModule(m.operators)); System.import('./src/app.component'); }); </script> </head> <body> <app-component></app-component> </body> </html>
<main class="container-fluid"> <wj-flex-grid #flexGrid [itemsSource]="data" > <wj-flex-grid-column *ngFor="let col of initialCols" [header]="col.header" [binding]="col.binding" [width]="col.width" [dataMap]="getDataMap(col.binding)" [editor]="getEditor(col.binding)" [cellTemplate]="getTemp(col.binding)" > <ng-container *ngIf="hasTemplate(col.binding)"> <ng-template wjFlexGridCellTemplate [cellType]="'Cell'" let-cell="cell"> <span [innerHTML]=" getTemplate(col.binding, cell) ? getTemplate(col.binding, cell).html : '' " ></span> </ng-template> </ng-container> </wj-flex-grid-column> <wj-flex-grid-filter #filter></wj-flex-grid-filter> </wj-flex-grid> <br /> <button class="btn btn-default" (click)="saveLayout()">Save State</button> <button class="btn btn-default" (click)="restoreLayout()"> Restore State </button> </main>
import * as wjcCore from '@mescius/wijmo'; // // data used to generate random items export function getData() { var countries = this.getCountries(), products = 'Widgets,Gadgets,Yahoos'.split(','), data = []; for (var i = 0; i < 30; i++) { data.push({ id: i, country: countries[i % countries.length].id, products: products[i % products.length], downloads: Math.round(Math.random() * 20000), button: '' }); } return data; } // export 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 function getProducts() { return [ { id: 0, name: 'Widgets' }, { id: 1, name: 'Gadgets' }, { id: 2, name: 'Yahoos' } ]; } // export function getCv(data: any) { var dataCv = new wjcCore.CollectionView(data); dataCv.groupDescriptions.push( new wjcCore.PropertyGroupDescription('country_editor') ); return dataCv; }
.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; }
(function (global) { SystemJS.config({ transpiler: './plugin-typescript.js', typescriptOptions: { "target": "ES2022", "module": "system", "emitDecoratorMetadata": true, "experimentalDecorators": true, }, baseURL: 'node_modules/', meta: { 'typescript': { "exports": "ts" }, '*.css': { loader: 'systemjs-plugin-css' }, '*.mjs': { format: 'esm' }, }, paths: { // paths serve as alias 'npm:': '' }, packageConfigPaths: [ '/node_modules/*/package.json', "/node_modules/@angular/*/package.json", "/node_modules/@mescius/*/package.json" ], map: { 'core-js': 'https://cdn.jsdelivr.net/npm/core-js@2.6.12/client/shim.min.js', 'typescript': 'https://cdnjs.cloudflare.com/ajax/libs/typescript/5.2.2/typescript.min.js', "rxjs": "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js", 'systemjs-plugin-css': 'https://cdn.jsdelivr.net/npm/systemjs-plugin-css@0.1.37/css.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.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.touch': 'npm:@mescius/wijmo.touch/index.js', '@mescius/wijmo.cloud': 'npm:@mescius/wijmo.cloud/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.angular2.chart.analytics": "npm:@mescius/wijmo.angular2.chart.analytics/index.js", "@mescius/wijmo.angular2.chart.animation": "npm:@mescius/wijmo.angular2.chart.animation/index.js", "@mescius/wijmo.angular2.chart.annotation": "npm:@mescius/wijmo.angular2.chart.annotation/index.js", "@mescius/wijmo.angular2.chart.finance.analytics": "npm:@mescius/wijmo.angular2.chart.finance.analytics/index.js", "@mescius/wijmo.angular2.chart.finance": "npm:@mescius/wijmo.angular2.chart.finance/index.js", "@mescius/wijmo.angular2.chart.hierarchical": "npm:@mescius/wijmo.angular2.chart.hierarchical/index.js", "@mescius/wijmo.angular2.chart.interaction": "npm:@mescius/wijmo.angular2.chart.interaction/index.js", "@mescius/wijmo.angular2.chart.radar": "npm:@mescius/wijmo.angular2.chart.radar/index.js", '@mescius/wijmo.angular2.chart.map': 'npm:@mescius/wijmo.angular2.chart.map/index.js', "@mescius/wijmo.angular2.chart": "npm:@mescius/wijmo.angular2.chart/index.js", "@mescius/wijmo.angular2.core": "npm:@mescius/wijmo.angular2.core/index.js", "@mescius/wijmo.angular2.gauge": "npm:@mescius/wijmo.angular2.gauge/index.js", "@mescius/wijmo.angular2.grid.detail": "npm:@mescius/wijmo.angular2.grid.detail/index.js", "@mescius/wijmo.angular2.grid.filter": "npm:@mescius/wijmo.angular2.grid.filter/index.js", "@mescius/wijmo.angular2.grid.grouppanel": "npm:@mescius/wijmo.angular2.grid.grouppanel/index.js", "@mescius/wijmo.angular2.grid.search": "npm:@mescius/wijmo.angular2.grid.search/index.js", "@mescius/wijmo.angular2.grid.multirow": "npm:@mescius/wijmo.angular2.grid.multirow/index.js", "@mescius/wijmo.angular2.grid.sheet": "npm:@mescius/wijmo.angular2.grid.sheet/index.js", '@mescius/wijmo.angular2.grid.transposed': 'npm:@mescius/wijmo.angular2.grid.transposed/index.js', '@mescius/wijmo.angular2.grid.transposedmultirow': 'npm:@mescius/wijmo.angular2.grid.transposedmultirow/index.js', "@mescius/wijmo.angular2.grid": "npm:@mescius/wijmo.angular2.grid/index.js", "@mescius/wijmo.angular2.input": "npm:@mescius/wijmo.angular2.input/index.js", "@mescius/wijmo.angular2.olap": "npm:@mescius/wijmo.angular2.olap/index.js", "@mescius/wijmo.angular2.viewer": "npm:@mescius/wijmo.angular2.viewer/index.js", "@mescius/wijmo.angular2.nav": "npm:@mescius/wijmo.angular2.nav/index.js", "@mescius/wijmo.angular2.directivebase": "npm:@mescius/wijmo.angular2.directivebase/index.js", '@mescius/wijmo.angular2.barcode.common': 'npm:@mescius/wijmo.angular2.barcode.common/index.js', '@mescius/wijmo.angular2.barcode.composite': 'npm:@mescius/wijmo.angular2.barcode.composite/index.js', '@mescius/wijmo.angular2.barcode.specialized': 'npm:@mescius/wijmo.angular2.barcode.specialized/index.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'jszip': 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js', '@angular/core': 'npm:@angular/core/fesm2022/core.mjs', '@angular/core/primitives/signals': 'npm:@angular/core/fesm2022/primitives/signals.mjs', '@angular/core/primitives/event-dispatch': 'npm:@angular/core/fesm2022/primitives/event-dispatch.mjs', '@angular/common': 'npm:@angular/common/fesm2022/common.mjs', '@angular/common/http': 'npm:@angular/common/fesm2022/http.mjs', '@angular/compiler': 'npm:@angular/compiler/fesm2022/compiler.mjs', '@angular/platform-browser': 'npm:@angular/platform-browser/fesm2022/platform-browser.mjs', '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/fesm2022/platform-browser-dynamic.mjs', '@angular/http': 'npm:@angular/http/fesm2022/http.mjs', '@angular/router': 'npm:@angular/router/fesm2022/router.mjs', '@angular/forms': 'npm:@angular/forms/fesm2022/forms.mjs', }, // packages tells the System loader how to load when no filename and/or no extension packages: { "./src": { defaultExtension: 'ts' }, "node_modules": { defaultExtension: 'js' }, wijmo: { defaultExtension: 'js', } } }); })(this);