ODataCollectionView with Server-based Grouping (Angular)

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

import 'bootstrap.css'; import '@mescius/wijmo.styles/wijmo.css'; import './styles.css'; // import '@angular/compiler'; import { Component, enableProdMode, ViewChild, ɵresolveComponentResources } from '@angular/core'; import { BrowserModule, bootstrapApplication } from '@angular/platform-browser'; import { ODataCollectionView } from '@mescius/wijmo.odata'; import { createElement } from '@mescius/wijmo'; import * as wjcGrid from '@mescius/wijmo.grid'; import { WjGridModule } from '@mescius/wijmo.angular2.grid'; import { WjGridFilterModule } from '@mescius/wijmo.angular2.grid.filter'; import { CollectionViewNavigator} from '@mescius/wijmo.input'; import { WjInputModule } from '@mescius/wijmo.angular2.input'; import { ElementRef } from '@angular/core'; // @Component({ standalone: true, imports: [WjGridModule, WjGridFilterModule, WjInputModule, BrowserModule], selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { customers: ODataCollectionView; // odata config url: string = "https://demodata.mescius.io/adventureworks/odata/v1/"; tableName: string = "SalesOrderHeaders"; fields: string[] = "AccountNumber,PurchaseOrderNumber,SalesOrderNumber,SubTotal,TaxAmt,Freight,TotalDue,ModifiedDate".split(','); // feature settings cvSettings = { fields: this.fields, groupOnServer: true, groupLazyLoading: true, groupDescriptions: ['AccountNumber'], pageSize: 10, loading:()=>this.toggleSpinner(this._flexGrid,true), loaded:()=>this.toggleSpinner(this._flexGrid,false) } @ViewChild('flex', { static: true }) private _flexGrid: wjcGrid.FlexGrid; @ViewChild('pager', { static: true }) private _pager: CollectionViewNavigator; @ViewChild('pageChecker',{static:true}) private pageChecker:ElementRef; constructor() { this.customers = new ODataCollectionView(this.url, this.tableName, this.cvSettings); } //show gif loader while data loading toggleSpinner(grid:wjcGrid.FlexGrid, show:boolean){ let _rect = grid.controlRect; let elem:HTMLElement = 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'; } toggleFeature(feature: string) { const pageChecker = this.pageChecker.nativeElement as HTMLInputElement; //default server grouping if (feature == 'groupServer') { this.cvSettings.groupLazyLoading = false; } //enable group lazy loading if (feature == "lazyload") { this.cvSettings.groupLazyLoading = true; } if(feature == 'paging'){ this.cvSettings.pageSize = pageChecker.checked ? 10 : 0; this._pager.hostElement.style.display = pageChecker.checked ? "inline-flex" : "none"; } // reset itemsSource const cv = new ODataCollectionView(this.url, this.tableName, this.cvSettings); this._flexGrid.itemsSource =cv; this._pager.cv = cv; } } // // 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 ODataCollectionView Binding</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"> <div id="feature-options" class="row"> <div class="col-sm-6"> <label for="lazyload"> <input id="lazyload" type="radio" name="feature" value="lazyLoadGrouping" checked (change)="toggleFeature('lazyload')"> Lazy-load grouping </label> <br /> <label for="groupServer"> <input id="groupServer" type="radio" name="feature" value="groupOnServer" (change)="toggleFeature('groupServer')"> Server-based group </label> </div> <div class="col-sm-6"> <label for="paging"> Paging <input #pageChecker id='paging' type="checkbox" checked (change)="toggleFeature('paging')"> </label> </div> </div> <wj-collection-view-navigator #pager [cv]="customers" [byPage]="true"></wj-collection-view-navigator> <!-- the grid --> <wj-flex-grid #flex [itemsSource]="customers" [isReadOnly]="true" > <wj-flex-grid-filter [defaultFilterType]="'Condition'"></wj-flex-grid-filter> <wj-flex-grid-column [binding]="'AccountNumber'" [header]="'Account Number'" [dataType]="'String'" [visible]="false"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'PurchaseOrderNumber'" [header]="'Purchase Order Number'" [dataType]="'String'" [width]="'*'" [minWidth]="200"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'SalesOrderNumber'" [header]="'Sales Order Number'" [dataType]="'String'" [width]="'*'" [minWidth]="200"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'SubTotal'" [header]="'Sub Total'" [dataType]="'Number'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'TaxAmt'" [header]="'Tax Amount'" [dataType]="'Number'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'Freight'" [header]="'Freight'" [dataType]="'Number'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'TotalDue'" [header]="'Total Due'" [dataType]="'Number'"></wj-flex-grid-column> <wj-flex-grid-column [binding]="'ModifiedDate'" [header]="'Modified Date'" [dataType]="'Date'" [format]="'d'"></wj-flex-grid-column> </wj-flex-grid> </main>
.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%; } label{ padding: 0 10px; } .wj-pager{ margin-bottom: 5px; }
(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/di': 'npm:@angular/core/fesm2022/primitives/di.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);