Cell Templates (React)

Wijmo interops for popular frameworks allow you to define custom content for grid cells declaratively, by using the frameworks' own template markup.

Such templates may include arbitrary components and HTML elements, with property and event bindings. All types of grid cells can be defined using templates (data cells, header cells, editors, etc).

This sample shows a grid with the templates for all cell types. The templates can be dynamically enabled or disabled by toggling the checkboxes.

This example uses React.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './app.css'; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjGrid from '@grapecity/wijmo.react.grid'; import * as wjInput from '@grapecity/wijmo.react.input'; import * as wjcCore from '@grapecity/wijmo'; import * as wjcGrid from '@grapecity/wijmo.grid'; import * as dataService from './data'; // class App extends React.Component { constructor(props) { super(props); // // The template used in more than one cell type is defined as a method. // The rest of the template functions are inlined in FlexGridCellTemplate // definitions. this.totalTemplate = (context) => { return <React.Fragment> Sum: {wjcCore.Globalize.formatNumber(context.value, 'N0')} </React.Fragment>; }; // Another template function. this.rowHeaderTemplate = (context) => context.row.index + 1; this.state = { itemsSource: dataService.getCv(dataService.getData()), countries: dataService.getCountries(), customTopLeft: true, customBottomLeft: true, customRowHeader: true, customRowHeaderEdit: true, customCell: true, customCellEdit: true, customGroupHeader: true, customColumnHeader: true, customGroup: true, customColumnFooter: true, highlightDownloads: true }; this.initialized = this.initialized.bind(this); this.totalTemplate = this.totalTemplate.bind(this); this.rowHeaderTemplate = this.rowHeaderTemplate.bind(this); } // // Wijmo event handlers initialized(grid, e) { grid.columnFooters.rows.push(new wjcGrid.GroupRow()); } // render() { return <div> <p> Use a <b>FlexGridCellTemplate</b> component to define a cell template. The <b>cellType</b> property specifies the type of cell represented by the template, and the <b>template</b> <i>render prop</i> accepts a function that renders the cells content. The function parameter receives an object with the cell-specific data, including the data item (<b>item</b>), row (<b>row</b>), and column (<b>col</b>) that the cell represents. </p> <p> Note that column-specific templates should be defined as children of the corresponding <b>FlexGridColumn</b> component, while the others are defined as children of the <b>FlexGrid</b> component. </p> <wjGrid.FlexGrid itemsSource={this.state.itemsSource} allowSorting={false} autoSizeMode="Both" allowResizing="Both" deferResizing={true} initialized={this.initialized}> <wjGrid.FlexGridCellTemplate cellType="TopLeft" template={this.state.customTopLeft ? (context) => '№' : null}/> {this.state.customBottomLeft ? <wjGrid.FlexGridCellTemplate cellType="BottomLeft" template={(context) => <span>&#931;</span>}/> : null} <wjGrid.FlexGridCellTemplate cellType="RowHeader" template={this.state.customRowHeader ? this.rowHeaderTemplate : null}/> <wjGrid.FlexGridCellTemplate cellType="RowHeaderEdit" template={this.state.customRowHeaderEdit ? (context) => '...' : null}/> <wjGrid.FlexGridColumn header="Country" binding="country" width="*"> <wjGrid.FlexGridCellTemplate cellType="Cell" template={this.state.customCell ? (context) => { return <React.Fragment> <img src={`resources/${context.item.country}.png`}/> {context.item.country} </React.Fragment>; } : null}/> <wjGrid.FlexGridCellTemplate cellType="CellEdit" template={this.state.customCellEdit ? (context) => { return <wjInput.ComboBox className="cell-editor" itemsSource={this.state.countries} isEditable={false} selectedValue={context.value} selectedIndexChanged={(cb) => context.value = cb.selectedValue}> </wjInput.ComboBox>; } : null}/> <wjGrid.FlexGridCellTemplate cellType="GroupHeader" template={this.state.customGroupHeader ? (context) => { return <React.Fragment> <input type="checkbox" checked={context.row.isCollapsed} onChange={e => context.row.isCollapsed = e.target.checked}/> {context.item.name} ({context.item.items.length} items) </React.Fragment>; } : null}/> </wjGrid.FlexGridColumn> <wjGrid.FlexGridColumn header="Downloads" binding="downloads" width={170} aggregate="Sum"> <wjGrid.FlexGridCellTemplate cellType="ColumnHeader" template={this.state.customColumnHeader ? (context) => { return <React.Fragment> <input type="checkbox" checked={this.state.highlightDownloads} onChange={(e) => { this.setState({ highlightDownloads: e.target.checked }); }}/> Downloads </React.Fragment>; } : null}/> <wjGrid.FlexGridCellTemplate cellType="Cell" template={this.state.customCell ? (context) => { let style = { fontWeight: 700, color: '' }, downloads = context.item.downloads; if (this.state.highlightDownloads) { style.color = downloads > 10000 ? 'green' : 'red'; } return <span style={style}> {downloads} </span>; } : null}/> <wjGrid.FlexGridCellTemplate cellType="CellEdit" template={this.state.customCellEdit ? (context) => { return <wjInput.InputNumber className="cell-editor" step={1} value={context.value} valueChanged={(inpNum) => context.value = inpNum.value}> </wjInput.InputNumber>; } : null}/> <wjGrid.FlexGridCellTemplate cellType="Group" template={this.state.customGroup ? this.totalTemplate : null}/> <wjGrid.FlexGridCellTemplate cellType="ColumnFooter" template={this.state.customColumnFooter ? this.totalTemplate : null}/> </wjGrid.FlexGridColumn> </wjGrid.FlexGrid> <div className="checkbox-list"> <div className="checkbox-list-title">Column level templates:</div> <div className="checkbox-cell"> <label className="checkbox"> <input type="checkbox" checked={this.state.customCell} onChange={e => this.setState({ customCell: e.target.checked })}/> Cell </label> <label className="checkbox"> <input type="checkbox" checked={this.state.customCellEdit} onChange={e => this.setState({ customCellEdit: e.target.checked })}/> CellEdit </label> </div> <div className="checkbox-cell"> <label className="checkbox"> <input type="checkbox" checked={this.state.customColumnHeader} onChange={e => this.setState({ customColumnHeader: e.target.checked })}/> ColumnHeader </label> <label className="checkbox"> <input type="checkbox" checked={this.state.customColumnFooter} onChange={e => this.setState({ customColumnFooter: e.target.checked })}/> ColumnFooter </label> </div> <div className="checkbox-cell"> <label className="checkbox"> <input type="checkbox" checked={this.state.customGroupHeader} onChange={e => this.setState({ customGroupHeader: e.target.checked })}/> GroupHeader </label> <label className="checkbox"> <input type="checkbox" checked={this.state.customGroup} onChange={e => this.setState({ customGroup: e.target.checked })}/> Group </label> </div> <div className="checkbox-list-title">Grid level templates:</div> <div className="checkbox-cell"> <label className="checkbox"> <input type="checkbox" checked={this.state.customTopLeft} onChange={e => this.setState({ customTopLeft: e.target.checked })}/> TopLeft </label> <label className="checkbox"> <input type="checkbox" checked={this.state.customBottomLeft} onChange={e => this.setState({ customBottomLeft: e.target.checked })}/> BottomLeft </label> </div> <div className="checkbox-cell"> <label className="checkbox"> <input type="checkbox" checked={this.state.customRowHeader} onChange={e => this.setState({ customRowHeader: e.target.checked })}/> RowHeader </label> <label className="checkbox"> <input type="checkbox" checked={this.state.customRowHeaderEdit} onChange={e => this.setState({ customRowHeaderEdit: e.target.checked })}/> RowHeaderEdit </label> </div> </div> </div>; } } ReactDOM.render(<App />, document.getElementById('app'));
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>Cell Templates</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.src.js"></script> <script src="systemjs.config.js"></script> <script> System.import('./src/app'); </script> </head> <body> <div id="app"></div> </body> </html>
body { margin-bottom: 24px; } label { margin-right: 3px; } .checkbox-list { padding: 0 20px; } .checkbox-cell { display: table-cell; padding-right: 50px; } .checkbox-list .checkbox { margin: 10px 0 0 0; } .checkbox-list-title { font-size: 18px; margin: 10px 0px 2px -20px; } .wj-flexgrid { max-height: 350px; max-width: 600px; margin: 10px 0; } .cell-editor { position: absolute; left: 0px; width: 100%; border-radius: 0px; margin: -4px 0 -3px 0; }
import * as wjcCore from '@grapecity/wijmo'; // // data used to generate random items export function getData() { var countries = this.getCountries(), data = []; for (var i = 0; i < 30; i++) { data.push({ id: i, date: new Date(2015, Math.floor(i / countries.length) % 12, (Math.floor(i / countries.length) + 1) % 28), country: countries[i % countries.length], countryMapped: i % countries.length, downloads: Math.round(Math.random() * 20000), sales: Math.random() * 10000, expenses: Math.random() * 5000, checked: i % 9 == 0 }); } return data; } // export function getCountries() { return 'US,Germany,UK,Japan,Italy,Greece'.split(','); } // export function getCv(data) { var dataCv = new wjcCore.CollectionView(data); dataCv.sortDescriptions.push(new wjcCore.SortDescription('date', true)); dataCv.groupDescriptions.push(new wjcCore.PropertyGroupDescription('country')); return dataCv; }
(function (global) { 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', '@grapecity/wijmo': 'npm:@grapecity/wijmo/index.js', '@grapecity/wijmo.input': 'npm:@grapecity/wijmo.input/index.js', '@grapecity/wijmo.styles': 'npm:@grapecity/wijmo.styles', '@grapecity/wijmo.cultures': 'npm:@grapecity/wijmo.cultures', '@grapecity/wijmo.chart': 'npm:@grapecity/wijmo.chart/index.js', '@grapecity/wijmo.chart.analytics': 'npm:@grapecity/wijmo.chart.analytics/index.js', '@grapecity/wijmo.chart.animation': 'npm:@grapecity/wijmo.chart.animation/index.js', '@grapecity/wijmo.chart.annotation': 'npm:@grapecity/wijmo.chart.annotation/index.js', '@grapecity/wijmo.chart.finance': 'npm:@grapecity/wijmo.chart.finance/index.js', '@grapecity/wijmo.chart.finance.analytics': 'npm:@grapecity/wijmo.chart.finance.analytics/index.js', '@grapecity/wijmo.chart.hierarchical': 'npm:@grapecity/wijmo.chart.hierarchical/index.js', '@grapecity/wijmo.chart.interaction': 'npm:@grapecity/wijmo.chart.interaction/index.js', '@grapecity/wijmo.chart.radar': 'npm:@grapecity/wijmo.chart.radar/index.js', '@grapecity/wijmo.chart.render': 'npm:@grapecity/wijmo.chart.render/index.js', '@grapecity/wijmo.chart.webgl': 'npm:@grapecity/wijmo.chart.webgl/index.js', '@grapecity/wijmo.gauge': 'npm:@grapecity/wijmo.gauge/index.js', '@grapecity/wijmo.grid': 'npm:@grapecity/wijmo.grid/index.js', '@grapecity/wijmo.grid.detail': 'npm:@grapecity/wijmo.grid.detail/index.js', '@grapecity/wijmo.grid.filter': 'npm:@grapecity/wijmo.grid.filter/index.js', '@grapecity/wijmo.grid.search': 'npm:@grapecity/wijmo.grid.search/index.js', '@grapecity/wijmo.grid.grouppanel': 'npm:@grapecity/wijmo.grid.grouppanel/index.js', '@grapecity/wijmo.grid.multirow': 'npm:@grapecity/wijmo.grid.multirow/index.js', '@grapecity/wijmo.grid.transposed': 'npm:@grapecity/wijmo.grid.transposed/index.js', '@grapecity/wijmo.grid.pdf': 'npm:@grapecity/wijmo.grid.pdf/index.js', '@grapecity/wijmo.grid.sheet': 'npm:@grapecity/wijmo.grid.sheet/index.js', '@grapecity/wijmo.grid.xlsx': 'npm:@grapecity/wijmo.grid.xlsx/index.js', '@grapecity/wijmo.grid.selector': 'npm:@grapecity/wijmo.grid.selector/index.js', '@grapecity/wijmo.grid.cellmaker': 'npm:@grapecity/wijmo.grid.cellmaker/index.js', '@grapecity/wijmo.grid.immutable': 'npm:@grapecity/wijmo.grid.immutable/index.js', '@grapecity/wijmo.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.nav': 'npm:@grapecity/wijmo.nav/index.js', '@grapecity/wijmo.odata': 'npm:@grapecity/wijmo.odata/index.js', '@grapecity/wijmo.olap': 'npm:@grapecity/wijmo.olap/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.viewer': 'npm:@grapecity/wijmo.viewer/index.js', '@grapecity/wijmo.xlsx': 'npm:@grapecity/wijmo.xlsx/index.js', '@grapecity/wijmo.undo': 'npm:@grapecity/wijmo.undo/index.js', '@grapecity/wijmo.interop.grid': 'npm:@grapecity/wijmo.interop.grid/index.js', "@grapecity/wijmo.react.chart.analytics": "npm:@grapecity/wijmo.react.chart.analytics/index.js", "@grapecity/wijmo.react.chart.animation": "npm:@grapecity/wijmo.react.chart.animation/index.js", "@grapecity/wijmo.react.chart.annotation": "npm:@grapecity/wijmo.react.chart.annotation/index.js", "@grapecity/wijmo.react.chart.finance.analytics": "npm:@grapecity/wijmo.react.chart.finance.analytics/index.js", "@grapecity/wijmo.react.chart.finance": "npm:@grapecity/wijmo.react.chart.finance/index.js", "@grapecity/wijmo.react.chart.hierarchical": "npm:@grapecity/wijmo.react.chart.hierarchical/index.js", "@grapecity/wijmo.react.chart.interaction": "npm:@grapecity/wijmo.react.chart.interaction/index.js", "@grapecity/wijmo.react.chart.radar": "npm:@grapecity/wijmo.react.chart.radar/index.js", "@grapecity/wijmo.react.chart": "npm:@grapecity/wijmo.react.chart/index.js", "@grapecity/wijmo.react.core": "npm:@grapecity/wijmo.react.core/index.js", "@grapecity/wijmo.react.gauge": "npm:@grapecity/wijmo.react.gauge/index.js", "@grapecity/wijmo.react.grid.detail": "npm:@grapecity/wijmo.react.grid.detail/index.js", "@grapecity/wijmo.react.grid.filter": "npm:@grapecity/wijmo.react.grid.filter/index.js", "@grapecity/wijmo.react.grid.grouppanel": "npm:@grapecity/wijmo.react.grid.grouppanel/index.js", '@grapecity/wijmo.react.grid.search': 'npm:@grapecity/wijmo.react.grid.search/index.js', "@grapecity/wijmo.react.grid.multirow": "npm:@grapecity/wijmo.react.grid.multirow/index.js", "@grapecity/wijmo.react.grid.sheet": "npm:@grapecity/wijmo.react.grid.sheet/index.js", '@grapecity/wijmo.react.grid.transposed': 'npm:@grapecity/wijmo.react.grid.transposed/index.js', '@grapecity/wijmo.react.grid.immutable': 'npm:@grapecity/wijmo.react.grid.immutable/index.js', "@grapecity/wijmo.react.grid": "npm:@grapecity/wijmo.react.grid/index.js", "@grapecity/wijmo.react.input": "npm:@grapecity/wijmo.react.input/index.js", "@grapecity/wijmo.react.olap": "npm:@grapecity/wijmo.react.olap/index.js", "@grapecity/wijmo.react.viewer": "npm:@grapecity/wijmo.react.viewer/index.js", "@grapecity/wijmo.react.nav": "npm:@grapecity/wijmo.react.nav/index.js", "@grapecity/wijmo.react.base": "npm:@grapecity/wijmo.react.base/index.js", 'jszip': 'npm:jszip/dist/jszip.js', 'react': 'npm:react/umd/react.production.min.js', 'react-dom': 'npm:react-dom/umd/react-dom.production.min.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' }, // packages tells the System loader how to load when no filename and/or no extension packages: { src: { defaultExtension: 'jsx' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);