FlexGrid Hierarchical Data

Object - FlexGrid Properties: - Hierarchical Data

by Joel Parks

HTML

<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="http://cdn.wijmo.com/5.latest/styles/wijmo.min.css">
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.input.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.grid.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.chart.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.grid.filter.min.js"></script>
<script src="http://cdn.wijmo.com/5.latest/controls/wijmo.odata.min.js"></script>
<div class="container">

  <h1>
    Filtering Hierarchical Data</h1>
  <p>
    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.</p>
  <p>
    Filtering hierarchical data is not a trivial exercise because
    when a child element is visible, all its ancestors should
    also be visible.</p>    
  <p>
    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:</p> 
  <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>
</div>

CSS

.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;
}

JavaScript

onload = function() {
  // Adds a text filter to the hierarchical grid
  document.getElementById('filter').addEventListener('input', function(e) {
    var filter = e.target.value.toLowerCase();
    applyHierarchicalFilter(theGrid, filter);
  });
  
  // Binds the grid to a Wijmo FlexGrid array
  var theGrid = new wijmo.grid.FlexGrid('#theGrid', {
    // Changes the header of the binded value to what is stored in the 'header' attribute
    // You can leave columns out of the table; the 'Active' column is left out
    autoGenerateColumns: false,
    childItemsPath: 'cities',
    headersVisibility: 'Column',
    itemsSource: getData(),
    columns: [{
      binding: "name",
      header: "NAME",
      width: "*"
    }, {
      binding: "type",
      header: "TYPE",
      // StarWidth expands/contracts a field so that the FlexGrid isn't too wide/narrow
      width: "*"
    }, {
      binding: "population",
      header: "POPULATION",
      width: "*"
    }]
  });

  // update row visibility
  function applyHierarchicalFilter(grid, filter) {
  	var rows = grid.rows;
		for (var i = 0; i < rows.length; i++) {
    	var 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 |= cityVisible;
					}

          // if at least one city is visible, the state is visible
         ...