ODataCollectionView with Server-based Grouping (React)

Wijmo includes an ODataCollectionView class that extends ODataCollectionView to provide support for OData sources.

To use the ODataCollectionView class, create a new instance passing the URL of the data service, the name of the table you want to access, any optional parameters such as the fields you want to retrieve and whether you want filtering, sorting, and paging to be performed on the server or on the client.

OData is an OASIS standard that defines a set of best practices for building and consuming RESTful APIs. OData adds aggregation support from OData 7.0 onwards.

ODataCollectionView class allows to perform server-based grouping. For this, groupOnServer should be set to true. The class would verify if grouping is supported by the OData API.

The error would be thrown and execution would be stop if the provided OData API does not support server-based grouping.

Addition to server-based grouping, ODataCollectionView class provides group lazy-loading supports. This feature would load only top level groups at first. The rest data would be loaded on demand by expanding the targetted group by calling lazyLoadGroup. This method call handled internally for grid instances. To enable group lazy-loading, groupLazyLoading property should be set to true.

Learn about FlexGrid | ODataCollectionView Documentation | FlexGrid API Reference

This example uses React.

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import ReactDOM from 'react-dom/client'; import React, { useRef, useState } from 'react'; import useEvent from 'react-use-event-hook'; import { createElement } from '@mescius/wijmo'; import * as wjGrid from '@mescius/wijmo.react.grid'; import { CollectionViewNavigator } from '@mescius/wijmo.react.input'; import { ODataCollectionView } from '@mescius/wijmo.odata'; import * as wjFilter from '@mescius/wijmo.react.grid.filter'; import './app.css'; const url = "https://demodata.mescius.io/adventureworks/odata/v1/"; const tableName = "SalesOrderHeaders"; const fields = "AccountNumber,PurchaseOrderNumber,SalesOrderNumber,SubTotal,TaxAmt,Freight,TotalDue,ModifiedDate".split(','); function App() { const [selectedFeature, setSelectedFeature] = useState('lazyload'); const flexRef = useRef(null); const inpCbRef = useRef(null); const pagerRef = useRef(null); const cvSettings = useRef({ fields: fields, sortOnServer: true, filterOnServer: true, groupOnServer: true, groupLazyLoading: true, groupDescriptions: ['AccountNumber'], pageSize: 10, loading: () => { toggleSpinner(flexRef.current, true); }, loaded: () => { toggleSpinner(flexRef.current, false); } }); const pagerInitialized = (pager) => { pagerRef.current = pager; }; const flexInitialized = useEvent((flexgrid) => { flexRef.current = flexgrid; //create ODataCollectionView instance const cv = new ODataCollectionView(url, tableName, cvSettings.current); flexRef.current.itemsSource = cv; pagerRef.current.cv = cv; }); const toggleFeature = (feature) => { setSelectedFeature(feature); const pageChecker = inpCbRef.current; //default server group if (feature == "groupServer") { cvSettings.current.groupLazyLoading = false; } //enable group lazy loading if (feature == "lazyload") { cvSettings.current.groupLazyLoading = true; } if (feature == "paging") { cvSettings.current.pageSize = pageChecker.checked ? 10 : 0; pagerRef.current.hostElement.style.display = pageChecker.checked ? "inline-flex" : "none"; } // reset itemsSource const cv = new ODataCollectionView(url, tableName, cvSettings.current); flexRef.current.itemsSource = cv; pagerRef.current.cv = cv; }; return (<main className="container-fluid"> <div id="feature-options" className='row'> <div className='col-sm-6'> <label htmlFor="lazyload"> <input id="lazyload" type="radio" name="feature" value="lazyLoadGrouping" defaultChecked={true} onChange={() => toggleFeature('lazyload')}/>{' '} Lazy-load grouping </label> <br /> <label htmlFor="groupServer"> <input id="groupServer" type="radio" name="feature" value="groupOnServer" onChange={() => toggleFeature('groupServer')}/>{' '} Server-based group </label> </div> <div className='col-sm-6'> <label htmlFor='paging'> Paging <input ref={inpCbRef} id="paging" type="checkbox" name="paging" value="paging" defaultChecked={true} onChange={(e) => toggleFeature('paging')}/> </label> </div> </div> <CollectionViewNavigator byPage={true} initialized={pagerInitialized}/> <wjGrid.FlexGrid isReadOnly={true} initialized={flexInitialized}> <wjFilter.FlexGridFilter defaultFilterType="Condition"/> <wjGrid.FlexGridColumn binding="AccountNumber" header="Account Number" dataType="String" visible={false}/> <wjGrid.FlexGridColumn binding="PurchaseOrderNumber" header="Purchase Order Number" dataType="String" width="*" minWidth={200}/> <wjGrid.FlexGridColumn binding="SalesOrderNumber" header="Sales Order Number" dataType="String" width="*" minWidth={200}/> <wjGrid.FlexGridColumn binding="SubTotal" header="Sub Total" dataType="Number"/> <wjGrid.FlexGridColumn binding="TaxAmt" header="Tax Amount" dataType="Number"/> <wjGrid.FlexGridColumn binding="Freight" header="Freight" dataType="Number"/> <wjGrid.FlexGridColumn binding="TotalDue" header="Total Due" dataType="Number"/> <wjGrid.FlexGridColumn binding="ModifiedDate" header="Modified Date" dataType="Date" format="d"/> </wjGrid.FlexGrid> </main>); } //show gif loader while data loading function toggleSpinner(grid, show) { let _rect = grid.controlRect; let elem = grid.hostElement.querySelector('.spinner'); if (!elem) elem = createElement(`<div class='spinner' style="left:${_rect.left}px;top:${_rect.top}px;width:${_rect.width}px;height:${_rect.height}px;"><img src="resources/Rounded%20blocks.gif"></div>`, grid.hostElement); elem.style.display = show ? 'block' : 'none'; } 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 Data Binding Basic</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 { height: 400px; margin: 6px 0; } .spinner{ position: absolute; z-index: 10; opacity: .5; } .spinner img { margin: auto; position: relative; top:40%; left:45%; }
(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);