Row Details (Vue)

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 will create a details section for each row that users can expand to see the rest of the data.

You can add anything you want to the detail row, including other grids. The sample below shows both how to display the data in another grid, as well as within standard HTML elements.

Learn about FlexGrid | FlexGrid API Reference

This example uses Vue.

<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="Category Name" binding="CategoryName"></wj-flex-grid-column> <wj-flex-grid-column header="Description" binding="Description" width="*"></wj-flex-grid-column> <wj-flex-grid-detail :isAnimated=true v-slot="ctx"> ID: <b>{{ctx.item.CategoryID}}</b><br /> Name: <b>{{ctx.item.CategoryName}}</b><br /> Description: <b>{{ctx.item.Description}}</b><br /> Products: <b>{{getProducts(ctx.item.CategoryID).length}} items</b><br /> <ol> <li v-for="p in getProducts(ctx.item.CategoryID)" v-bind:key="p.ProductID">{{p.ProductName}}</li> </ol> </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 v-slot="ctx" :detailVisibilityMode="detailVisibilityMode" :maxHeight="maxHeight" :isAnimated="isAnimated" :keyActionEnter="keyActionEnter" :rowHasDetail="evenRowHasDetail ? rowHasDetailFn : null"> <wj-flex-grid :itemsSource="getProducts(ctx.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> </wj-flex-grid-detail> </wj-flex-grid> <div class="grid-options"> <wj-menu :header="'detailVisibilityMode'" :value="detailVisibilityMode" :itemClicked="itemClicked.bind(this,'detailVisibilityMode')"> <wj-menu-item :value="'Code'">Code</wj-menu-item> <wj-menu-item :value="'Selection'">Selection</wj-menu-item> <wj-menu-item :value="'ExpandSingle'">ExpandSingle</wj-menu-item> <wj-menu-item :value="'ExpandMulti'">ExpandMulti</wj-menu-item> </wj-menu> <wj-menu :header="'maxHeight'" :value="maxHeight" :itemClicked="itemClicked.bind(this,'maxHeight')"> <wj-menu-item :value="0">null</wj-menu-item> <wj-menu-item :value="100">100</wj-menu-item> <wj-menu-item :value="200">200</wj-menu-item> <wj-menu-item :value="300">300</wj-menu-item> <wj-menu-item :value="400">400</wj-menu-item> <wj-menu-item :value="500">500</wj-menu-item> </wj-menu> <wj-menu :header="'isAnimated'" :value="isAnimated" :itemClicked="itemClicked.bind(this,'isAnimated')"> <wj-menu-item :value="true">True</wj-menu-item> <wj-menu-item :value="false">False</wj-menu-item> </wj-menu> <wj-menu :header="'keyActionEnter'" :value="keyActionEnter" :itemClicked="itemClicked.bind(this,'keyActionEnter')"> <wj-menu-item :value="'None'">None</wj-menu-item> <wj-menu-item :value="'ToggleDetail'">ToggleDetail</wj-menu-item> </wj-menu> <wj-menu :header="'rowDetails'" :value="evenRowHasDetail" :itemClicked="itemClicked.bind(this,'evenRowHasDetail')"> <wj-menu-item :value="false">All</wj-menu-item> <wj-menu-item :value="true">Even rows only</wj-menu-item> </wj-menu> </div> </div> </template> <script> import "@grapecity/wijmo.styles/wijmo.css"; import "bootstrap.css"; import Vue from "vue"; import { ODataCollectionView } from "@grapecity/wijmo.odata"; import * as wjGridDetail from "@grapecity/wijmo.grid.detail"; import "@grapecity/wijmo.vue2.grid"; import "@grapecity/wijmo.vue2.grid.detail"; import "@grapecity/wijmo.vue2.input"; let App = Vue.extend({ name: "app", data: function() { return { categories : [], products : [], detailVisibilityMode: 'ExpandSingle', maxHeight: 0, isAnimated: true, keyActionEnter: 'None', evenRowHasDetail: false, }; }, methods: { getProducts(categoryID) { let categoryProducts = this._catToProductMap.get(categoryID); if (!categoryProducts) { categoryProducts = this.products.items.filter( (product) => product.CategoryID === categoryID); this._catToProductMap.set(categoryID, categoryProducts); } return categoryProducts; }, itemClicked: function(prop, s, e) { if (s.selectedIndex > -1) { this[prop] = s.selectedValue; } }, }, beforeCreate: function () { this.rowHasDetailFn = row => !(row.dataItem.CategoryID % 2); }, mounted: function () { const url = 'https://services.odata.org/Northwind/Northwind.svc'; this.categories = new ODataCollectionView(url, 'Categories', { fields: ['CategoryID', 'CategoryName', 'Description'] }); this.products = new ODataCollectionView(url, 'Products'); this._catToProductMap = new Map(); }, }); new Vue({ render: h => h(App) }).$mount("#app"); </script> <style> .wj-flexgrid { max-height: 350px; } body { margin-bottom: 20pt; } .grid-options .wj-menu { margin: 0.25em 0.5em 0.25em 0; } </style>
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Row Details</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>
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: true }, meta: { '*.css': { loader: 'css' }, '*.vue': { loader: 'vue-loader' } //'*.vue': { loader: 'systemjs-plugin-vue' } }, paths: { // paths serve as alias 'npm:': 'node_modules/' }, // map tells the System loader where to look for things map: { '@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.chart.map': 'npm:@grapecity/wijmo.chart.map/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.transposedmultirow': 'npm:@grapecity/wijmo.grid.transposedmultirow/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.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.rest': 'npm:@grapecity/wijmo.rest/index.js', '@grapecity/wijmo.pdf': 'npm:@grapecity/wijmo.pdf/index.js', '@grapecity/wijmo.pdf.security': 'npm:@grapecity/wijmo.pdf.security/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.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', '@grapecity/wijmo.barcode': 'npm:@grapecity/wijmo.barcode/index.js', '@grapecity/wijmo.barcode.common': 'npm:@grapecity/wijmo.barcode.common/index.js', '@grapecity/wijmo.barcode.composite': 'npm:@grapecity/wijmo.barcode.composite/index.js', '@grapecity/wijmo.barcode.specialized': 'npm:@grapecity/wijmo.barcode.specialized/index.js', "@grapecity/wijmo.vue2.chart.analytics": "npm:@grapecity/wijmo.vue2.chart.analytics/index.js", "@grapecity/wijmo.vue2.chart.animation": "npm:@grapecity/wijmo.vue2.chart.animation/index.js", "@grapecity/wijmo.vue2.chart.annotation": "npm:@grapecity/wijmo.vue2.chart.annotation/index.js", "@grapecity/wijmo.vue2.chart.finance.analytics": "npm:@grapecity/wijmo.vue2.chart.finance.analytics/index.js", "@grapecity/wijmo.vue2.chart.finance": "npm:@grapecity/wijmo.vue2.chart.finance/index.js", "@grapecity/wijmo.vue2.chart.hierarchical": "npm:@grapecity/wijmo.vue2.chart.hierarchical/index.js", "@grapecity/wijmo.vue2.chart.interaction": "npm:@grapecity/wijmo.vue2.chart.interaction/index.js", "@grapecity/wijmo.vue2.chart.radar": "npm:@grapecity/wijmo.vue2.chart.radar/index.js", '@grapecity/wijmo.vue2.chart.map': 'npm:@grapecity/wijmo.vue2.chart.map/index.js', "@grapecity/wijmo.vue2.chart": "npm:@grapecity/wijmo.vue2.chart/index.js", "@grapecity/wijmo.vue2.core": "npm:@grapecity/wijmo.vue2.core/index.js", "@grapecity/wijmo.vue2.gauge": "npm:@grapecity/wijmo.vue2.gauge/index.js", "@grapecity/wijmo.vue2.grid.detail": "npm:@grapecity/wijmo.vue2.grid.detail/index.js", "@grapecity/wijmo.vue2.grid.filter": "npm:@grapecity/wijmo.vue2.grid.filter/index.js", "@grapecity/wijmo.vue2.grid.grouppanel": "npm:@grapecity/wijmo.vue2.grid.grouppanel/index.js", '@grapecity/wijmo.vue2.grid.search': 'npm:@grapecity/wijmo.vue2.grid.search/index.js', "@grapecity/wijmo.vue2.grid.multirow": "npm:@grapecity/wijmo.vue2.grid.multirow/index.js", "@grapecity/wijmo.vue2.grid.sheet": "npm:@grapecity/wijmo.vue2.grid.sheet/index.js", '@grapecity/wijmo.vue2.grid.transposed': 'npm:@grapecity/wijmo.vue2.grid.transposed/index.js', '@grapecity/wijmo.vue2.grid.transposedmultirow': 'npm:@grapecity/wijmo.vue2.grid.transposedmultirow/index.js', "@grapecity/wijmo.vue2.grid": "npm:@grapecity/wijmo.vue2.grid/index.js", "@grapecity/wijmo.vue2.input": "npm:@grapecity/wijmo.vue2.input/index.js", "@grapecity/wijmo.vue2.olap": "npm:@grapecity/wijmo.vue2.olap/index.js", "@grapecity/wijmo.vue2.viewer": "npm:@grapecity/wijmo.vue2.viewer/index.js", "@grapecity/wijmo.vue2.nav": "npm:@grapecity/wijmo.vue2.nav/index.js", "@grapecity/wijmo.vue2.base": "npm:@grapecity/wijmo.vue2.base/index.js", '@grapecity/wijmo.vue2.barcode.common': 'npm:@grapecity/wijmo.vue2.barcode.common/index.js', '@grapecity/wijmo.vue2.barcode.composite': 'npm:@grapecity/wijmo.vue2.barcode.composite/index.js', '@grapecity/wijmo.vue2.barcode.specialized': 'npm:@grapecity/wijmo.vue2.barcode.specialized/index.js', 'bootstrap.css': 'npm:bootstrap/dist/css/bootstrap.min.css', 'jszip': 'npm:jszip/dist/jszip.js', 'css': 'npm:systemjs-plugin-css/css.js', 'vue': 'npm:vue/dist/vue.min.js', 'vue-loader': 'npm:systemjs-vue-browser/index.js', //'systemjs-plugin-vue': 'npm:systemjs-plugin-vue/index.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: 'js' }, rxjs: { defaultExtension: 'js' }, "node_modules": { defaultExtension: 'js' }, wijmo: { defaultExtension: 'js', } } }); })(this);