Row Details

Sometimes rows are bound to data objects that contain more information than would fit easily on a regular grid. In these scenarios, you may want to use the FlexGridDetailProvider class that is included with the wijmo.grid.detail module. The FlexGridDetailProvider extends the FlexGrid by adding collapse/expand buttons to row headers, and a createDetailCell method you can use to provide additional details about an item.

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import * as wjOdata from '@grapecity/wijmo.odata'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjGridDetail from '@grapecity/wijmo.grid.detail'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // // get OData categories and products var url = 'https://services.odata.org/Northwind/Northwind.svc'; var categories = new wjOdata.ODataCollectionView(url, 'Categories', { fields: ['CategoryID', 'CategoryName', 'Description'] }); var products = new wjOdata.ODataCollectionView(url, 'Products'); // // shared column definitions var categoryColumns = [ { binding: 'CategoryName', header: 'Category Name', width: '*' }, { binding: 'Description', header: 'Description', width: '2*' } ]; // // get products for a given category function getProducts(categoryID) { var arr = []; products.items.forEach(function (product) { if (product.CategoryID == categoryID) { arr.push(product); } }); return arr; } // // grid with HTML detail var htmlDetail = new wjGrid.FlexGrid('#htmlDetail', { autoGenerateColumns: false, columns: categoryColumns, itemsSource: categories, isReadOnly: true }); // // html detail provider var dpHtml = new wjGridDetail.FlexGridDetailProvider(htmlDetail, { // // use animation when showing details isAnimated: true, // // create detail cells for a given row createDetailCell: function (row) { // // build detail content for the current category var cat = row.dataItem; var prods = getProducts(cat.CategoryID); var html = 'ID: <b>' + cat.CategoryID + '</b><br/>'; html += 'Name: <b>' + cat.CategoryName + '</b><br/>'; html += 'Description: <b>' + cat.Description + '</b><br/>'; html += 'Products: <b>' + prods.length + ' items</b><br/>'; html += '<ol>'; prods.forEach(function (product) { html += '<li>' + product.ProductName + '</li>'; }); html += '</ol>'; // // create and return detail cell var cell = document.createElement('div'); cell.innerHTML = html; return cell; } }); // // grid with grid detail var gridDetail = new wjGrid.FlexGrid('#gridDetail', { autoGenerateColumns: false, columns: categoryColumns, itemsSource: categories, isReadOnly: true }); // // grid detail provider var dpGrid = new wjGridDetail.FlexGridDetailProvider(gridDetail, { // // use animation when showing details isAnimated: true, // // limit height of detail rows maxHeight: 150, // // create detail cells for a given row createDetailCell: function (row) { var cell = document.createElement('div'); var detailGrid = new wjGrid.FlexGrid(cell, { headersVisibility: wjGrid.HeadersVisibility.Column, isReadOnly: true, autoGenerateColumns: false, itemsSource: getProducts(row.dataItem.CategoryID), columns: [ { header: 'ID', binding: 'ProductID' }, { header: 'Name', binding: 'ProductName' }, { header: 'Qty/Unit', binding: 'QuantityPerUnit' }, { header: 'Unit Price', binding: 'UnitPrice' }, { header: 'Discontinued', binding: 'Discontinued' } ] }); return cell; } }); // } <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Nested Grids</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 class="container-fluid"> <h3> HTML in Row Details</h3> <p> This grid shows product categories on each row. Expanding the rows shows an HTML element with information about the products in that category:</p> <div id="htmlDetail"></div> <h3> Grids in Row Details</h3> <p> You can add anything you want to the detail rows, including other grids. This example shows the same categories, but the detail row uses another grid to show the products:</p> <div id="gridDetail"></div> </div> </body> </html> .wj-flexgrid { max-height: 350px; } body { margin-bottom: 20pt; } import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; // import * as wjOdata from '@grapecity/wijmo.odata'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjGridDetail from '@grapecity/wijmo.grid.detail'; import { Component, enableProdMode, NgModule, ChangeDetectionStrategy } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { BrowserModule } from '@angular/platform-browser'; import { WjGridModule } from '@grapecity/wijmo.angular2.grid'; import { WjGridDetailModule } from '@grapecity/wijmo.angular2.grid.detail'; // const url = 'https://services.odata.org/Northwind/Northwind.svc'; // @Component({ selector: 'app-component', templateUrl: 'src/app.component.html' }) export class AppComponent { categories: wjOdata.ODataCollectionView; products: wjOdata.ODataCollectionView; catToProductMap: Map<string, any> = new Map(); // DataSvc will be passed by derived classes constructor() { this.categories = this._getCategories(); this.products = this._getProducts(); } getProducts(categoryID: string) { let categoryProducts = this.catToProductMap.get(categoryID); if (!categoryProducts) { categoryProducts = this.products.items.filter( (product) => product.CategoryID === categoryID); this.catToProductMap.set(categoryID, categoryProducts); } return categoryProducts; } private _getCategories() { return new wjOdata.ODataCollectionView(url, 'Categories', { fields: ['CategoryID', 'CategoryName', 'Description'] }); } private _getProducts() { return new wjOdata.ODataCollectionView(url, 'Products'); } } // @NgModule({ imports: [WjGridModule, WjGridDetailModule, BrowserModule], declarations: [AppComponent], bootstrap: [AppComponent] }) export class AppModule { } // enableProdMode(); // Bootstrap application with hash style navigation and global services. platformBrowserDynamic().bootstrapModule(AppModule); <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Nested Grids</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/dist/zone.min.js"></script> <!-- SystemJS --> <script src="node_modules/systemjs/dist/system.js"></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> <div class="container-fluid"> <h3> HTML in Row Details</h3> <p> This grid shows product categories on each row. Expanding the rows shows an HTML element with information about the products in that category:</p> <wj-flex-grid [itemsSource]="categories" [isReadOnly]="true"> <wj-flex-grid-column [header]="'Name'" [binding]="'CategoryName'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Description'" [binding]="'Description'" [width]="'*'"></wj-flex-grid-column> <ng-template wjFlexGridDetail [isAnimated]=true let-item="item"> ID: <b>{{item.CategoryID}}</b><br /> Name: <b>{{item.CategoryName}}</b><br /> Description: <b>{{item.Description}}</b><br /> Products: <b>{{getProducts(item.CategoryID).length}} items</b><br /> <ol> <li *ngFor="let p of getProducts(item.CategoryID)">{{p.ProductName}}</li> </ol> </ng-template> </wj-flex-grid> <h3> Grids in Row Details</h3> <p> You can add anything you want to the detail rows, including other grids. This example shows the same categories, but the detail row uses another grid to show the products:</p> <wj-flex-grid #gridDetail [autoGenerateColumns]=false [itemsSource]="categories" [isReadOnly]=true> <wj-flex-grid-column [header]="'Category Name'" [binding]="'CategoryName'" [width]="'*'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Description'" [binding]="'Description'" [width]="'2*'"></wj-flex-grid-column> <ng-template wjFlexGridDetail [maxHeight]="250" let-item="item"> <wj-flex-grid [itemsSource]="getProducts(item.CategoryID)" [isReadOnly]="true" [headersVisibility]="'Column'"> <wj-flex-grid-column [header]="'ID'" [binding]="'ProductID'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Name'" [binding]="'ProductName'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Qty/Unit'" [binding]="'QuantityPerUnit'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Unit Price'" [binding]="'UnitPrice'"></wj-flex-grid-column> <wj-flex-grid-column [header]="'Discontinued'" [binding]="'Discontinued'"></wj-flex-grid-column> </wj-flex-grid> </ng-template> </wj-flex-grid> </div> .wj-flexgrid { max-height: 350px; } body { margin-bottom: 20pt; } <script> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import Vue from "vue"; import * as wjOdata from '@grapecity/wijmo.odata'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjGridDetail from '@grapecity/wijmo.grid.detail'; import "@grapecity/wijmo.vue2.grid"; import "@grapecity/wijmo.vue2.grid.detail"; let App = Vue.extend({ name: "app", template: `<div class="container-fluid"> <h3> HTML in Row Details</h3> <p> This grid shows product categories on each row. Expanding the rows shows an HTML element with information about the products in that category:</p> <wj-flex-grid :itemsSource="categories" :isReadOnly=true> <wj-flex-grid-column header="Name" binding="CategoryName"></wj-flex-grid-column> <wj-flex-grid-column header="Description" binding="Description" width="*"></wj-flex-grid-column> <wj-flex-grid-detail :initialized="initializeCategory" :isAnimated=true> <template slot-scope="props"> ID: <b>{{props.item.CategoryID}}</b><br /> Name: <b>{{props.item.CategoryName}}</b><br /> Description: <b>{{props.item.Description}}</b><br /> Products: <b>{{getProducts(props.item.CategoryID).length}} items</b><br /> <ol> <li v-for="p in getProducts(props.item.CategoryID)" v-bind:key="p">{{p.ProductName}}</li> </ol> </template> </wj-flex-grid-detail> </wj-flex-grid> <h3> Grids in Row Details</h3> <p> You can add anything you want to the detail rows, including other grids. This example shows the same categories, but the detail row uses another grid to show the products:</p> <wj-flex-grid :autoGenerateColumns=false :itemsSource="categories" :isReadOnly=true> <wj-flex-grid-column header="Category Name" binding="CategoryName" width="*"></wj-flex-grid-column> <wj-flex-grid-column header="Description" binding="Description" width="2*"></wj-flex-grid-column> <wj-flex-grid-detail :maxHeight=250> <template slot-scope="props"> <wj-flex-grid :itemsSource="getProducts(props.item.CategoryID)" :isReadOnly="true" headersVisibility="Column"> <wj-flex-grid-column header="ID" binding="ProductID"></wj-flex-grid-column> <wj-flex-grid-column header="Name" binding="ProductName"></wj-flex-grid-column> <wj-flex-grid-column header="Qty/Unit" binding="QuantityPerUnit"></wj-flex-grid-column> <wj-flex-grid-column header="Unit Price" binding="UnitPrice"></wj-flex-grid-column> <wj-flex-grid-column header="Discontinued" binding="Discontinued"></wj-flex-grid-column> </wj-flex-grid> </template> </wj-flex-grid-detail> </wj-flex-grid> </div>`, data: function() { return { categories : null, products : null }; }, methods: { initializeCategory: function(ctl){ this.wjFlexGridDetail = ctl; }, getProducts(categoryID) { var arr = []; this.products.items.forEach(function (product) { if (product.CategoryID == categoryID) { arr.push(product); } }); return arr; } }, mounted: function() { var url = 'https://services.odata.org/Northwind/Northwind.svc'; this.categories = new wjOdata.ODataCollectionView(url, 'Categories', { fields: ['CategoryID', 'CategoryName', 'Description'] }); this.products = new wjOdata.ODataCollectionView(url, 'Products'); } }); new Vue({ render: h => h(App) }).$mount("#app"); </script> <style> .wj-flexgrid { max-height: 350px; } body { margin-bottom: 20pt; } </style> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Nested Grids</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.vue'); </script> </head> <body> <div id="app"> </div> </body> </html> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import "./app.css"; // import * as React from 'react'; import * as ReactDOM from 'react-dom'; // import * as wjOdata from '@grapecity/wijmo.odata'; import * as wjGrid from '@grapecity/wijmo.grid'; import * as wjGridDetail from '@grapecity/wijmo.grid.detail'; import "@grapecity/wijmo.grid"; import "@grapecity/wijmo.grid.detail"; class App extends React.Component { constructor(props) { super(props); this.categoryColumns = []; var url = 'https://services.odata.org/Northwind/Northwind.svc'; this.state = { categories: new wjOdata.ODataCollectionView(url, 'Categories', { fields: ['CategoryID', 'CategoryName', 'Description'] }), products: new wjOdata.ODataCollectionView(url, 'Products') }; this.categoryColumns = [ { binding: 'CategoryName', header: 'Category Name', width: '*' }, { binding: 'Description', header: 'Description', width: '2*' } ]; } render() { return <div className="container-fluid"> <h3> HTML in Row Details</h3> <p> This grid shows product categories on each row. Expanding the rows shows an HTML element with information about the products in that category:</p> <div id="htmlDetail"></div> <h3> Grids in Row Details</h3> <p> You can add anything you want to the detail rows, including other grids. This example shows the same categories, but the detail row uses another grid to show the products:</p> <div id="gridDetail"></div> </div>; } componentDidMount() { var htmlDetail = new wjGrid.FlexGrid('#htmlDetail', { autoGenerateColumns: false, columns: this.categoryColumns, itemsSource: this.state.categories, isReadOnly: true }); new wjGridDetail.FlexGridDetailProvider(htmlDetail, { // // use animation when showing details isAnimated: true, // // create detail cells for a given row createDetailCell: (row) => { // // build detail content for the current category var cat = row.dataItem; var prods = this.getProducts(cat.CategoryID); var html = 'ID: <b>' + cat.CategoryID + '</b><br/>'; html += 'Name: <b>' + cat.CategoryName + '</b><br/>'; html += 'Description: <b>' + cat.Description + '</b><br/>'; html += 'Products: <b>' + prods.length + ' items</b><br/>'; html += '<ol>'; prods.forEach(function (product) { html += '<li>' + product.ProductName + '</li>'; }); html += '</ol>'; // // create and return detail cell var cell = document.createElement('div'); cell.innerHTML = html; return cell; } }); var gridDetail = new wjGrid.FlexGrid('#gridDetail', { autoGenerateColumns: false, columns: this.categoryColumns, itemsSource: this.state.categories, isReadOnly: true }); // grid detail provider new wjGridDetail.FlexGridDetailProvider(gridDetail, { // // use animation when showing details isAnimated: true, // // limit height of detail rows maxHeight: 150, // // create detail cells for a given row createDetailCell: (row) => { var cell = document.createElement('div'); var detailGrid = new wjGrid.FlexGrid(cell, { headersVisibility: wjGrid.HeadersVisibility.Column, isReadOnly: true, autoGenerateColumns: false, itemsSource: this.getProducts(row.dataItem.CategoryID), columns: [ { header: 'ID', binding: 'ProductID' }, { header: 'Name', binding: 'ProductName' }, { header: 'Qty/Unit', binding: 'QuantityPerUnit' }, { header: 'Unit Price', binding: 'UnitPrice' }, { header: 'Discontinued', binding: 'Discontinued' } ] }); return cell; } }); } getProducts(categoryID) { var arr = []; this.state.products.items.forEach(function (product) { if (product.CategoryID == categoryID) { arr.push(product); } }); return arr; } } 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>GrapeCity Wijmo FlexGrid Nested Grids</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> .wj-flexgrid { max-height: 350px; } body { margin-bottom: 20pt; }