Filtering Hierarchical Data

The CollectionView class supports filtering only for items that are direct children of the collection. In most cases, it does not work well for hierarchical data. Filtering hierarchical data is not a trivial exercise because when a child element is visible, all its ancestors should also be visible. The grid below shows how you can implement a simple hierarchical binding method that will show cities that match the filter and states that match the filter or contain cities that do. For example, try typing 'San' in the filter box:

import 'bootstrap.css'; import '@grapecity/wijmo.styles/wijmo.css'; import './styles.css'; import { FlexGrid } from '@grapecity/wijmo.grid'; import { getData } from './data'; // document.readyState === 'complete' ? init() : window.onload = init; // function init() { // // create tree-style grid var theGrid = new FlexGrid('#theGrid', { childItemsPath: 'cities', headersVisibility: 'Column', itemsSource: getData(), sortedColumn: applyHierarchicalFilter }); // // update filter document.getElementById('filter').addEventListener('input', function (e) { applyHierarchicalFilter(); }); // // update row visibility function applyHierarchicalFilter() { let input = document.getElementById('filter'), filter = input.value.toLowerCase(), grid = theGrid, rows = grid.rows; for (let i = 0; i < rows.length; i++) { let row = rows[i], state = row.dataItem, rng = row.getCellRange(); // // handle states (level 0) if (row.level == 0) { // // check if the state name matches the filter var stateVisible = state.name.toLowerCase().indexOf(filter) >= 0; if (stateVisible) { // // it does, so show the state and all its cities for (var j = rng.topRow; j <= rng.bottomRow; j++) { rows[j].visible = true; } // } else { // // it does not, so check the cities for (var j = rng.topRow + 1; j <= rng.bottomRow; j++) { var city = rows[j].dataItem, cityVisible = city.name.toLowerCase().indexOf(filter) >= 0; rows[j].visible = cityVisible; stateVisible = stateVisible || cityVisible; } // // if at least one city is visible, the state is visible rows[i].visible = stateVisible; } // // move on to the next group i = rng.bottomRow; } } } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>GrapeCity Wijmo FlexGrid Filtering Hierarchical Data</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"> <div class="input-group"> <div class="input-group-addon"><span class="glyphicon glyphicon-search"></span></div> <input id="filter" class="form-control" placeholder="Filter" /> </div> <div id="theGrid"> </div> <p> For more details in hierarchical filtering, please see our <a href="https://wijmo.com/blog/filter-hierarchical-data-flexgrid/" target="_blank">How to Filter Hierarchical Data in FlexGrid and Angular</a> blog.</p> </div> </body> </html>
// some hierarchical data export function getData() { return [ { name: 'Washington', type: 'state', population: 6971, cities: [ { name: 'Seattle', type: 'city', population: 652 }, { name: 'Spokane', type: 'city', population: 210 } ] }, { name: 'Oregon', type: 'state', population: 3930, cities: [ { name: 'Portland', type: 'city', population: 609 }, { name: 'Eugene', type: 'city', population: 159 } ] }, { name: 'California', type: 'state', population: 38330, cities: [ { name: 'Los Angeles', type: 'city', population: 3884 }, { name: 'San Diego', type: 'city', population: 1356 }, { name: 'San Francisco', type: 'city', population: 837 } ] } ]; }
.wj-flexgrid { max-height: 250px; margin: 10px 0; } .wj-cell.wj-group { border: none; } .wj-cell.wj-group:not(.wj-state-selected):not(.wj-state-multi-selected) { background-color: white; } body { margin-bottom: 20px; }
(function (global) { System.config({ transpiler: 'plugin-babel', babelOptions: { es2015: 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.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.touch': 'npm:@grapecity/wijmo.touch/index.js', '@grapecity/wijmo.cloud': 'npm:@grapecity/wijmo.cloud/index.js', 'jszip': 'npm:jszip/dist/jszip.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: 'js' }, "node_modules": { defaultExtension: 'js' }, } }); })(this);