Filtering Hierarchical Data

The CollectionView class supports filtering only for items that are direct children of the collection. In most of the 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 below:

Name
Type
Population
Washington
state
6,971
Seattle
city
652
Spokane
city
210
Oregon
state
3,930
Portland
city
609
Eugene
city
159
California
state
38,330
Los Angeles
city
3,884
San Diego
city
1,356
San Francisco
city
837

For more details on hierarchical filtering, please see our How to Filter Hierarchical Data in FlexGrid and Angular blog.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// This file locates: "Scripts/Lesson/C1FlexGrid/HierarchicalDataFiltering.js".
c1.documentReady(function () {
    var theGrid = wijmo.Control.getControl('#theGrid');
    theGrid.childItemsPath = "cities";
    theGrid.itemsSource = getData();
 
    // update filter
    document.getElementById('filter').addEventListener('input', function (e) {
        var filter = e.target.value.toLowerCase();
        applyHierarchicalFilter(theGrid, filter);
    });
 
    // 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
                    rows[i].visible = stateVisible;
                }
 
                // move on to the next group
                i = rng.bottomRow;
            }
        }
    }
 
    // some hierarchical data
    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 }]
          }
        ];
    }
});
1
2
3
4
5
6
7
// This file locates: "Content/css/Lesson/C1FlexGrid/HierarchicalDataFiltering.css".
.wj-cell.wj-group {
  border: none;
}
.wj-cell.wj-group:not(.wj-state-selected):not(.wj-state-multi-selected) {
  background-color: white;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
using System.Web.Mvc;
 
namespace LearnMvcClient.Controllers
{
    public partial class C1FlexGridController : Controller
    {
        // GET: HierarchicalDataFiltering
        public ActionResult HierarchicalDataFiltering()
        {
            return View();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<h1>
    @Html.Raw(Resources.C1FlexGrid.HierarchicalDataFiltering_Title)
</h1>
<p>
    @Html.Raw(Resources.C1FlexGrid.HierarchicalDataFiltering_Text1)
</p>
<p>
    @Html.Raw(Resources.C1FlexGrid.HierarchicalDataFiltering_Text2)
</p>
<p>
    @Html.Raw(Resources.C1FlexGrid.HierarchicalDataFiltering_Text3)
</p>
<div class="input-group">
    <div class="input-group-addon"><span class="glyphicon glyphicon-search"></span></div>
    <input id="filter" class="form-control" placeholder="@Resources.C1FlexGrid.HierarchicalDataFiltering_Text4">
</div>
@Html.C1().FlexGrid().Id("theGrid").HeadersVisibility(C1.Web.Mvc.Grid.HeadersVisibility.Column).Height(250)
 
<p>
    @Html.Raw(Resources.C1FlexGrid.HierarchicalDataFiltering_Text5)
</p>