a suite of UI Components for development of web apps
Advanced User Interface Controls and Components
Created: 16 July 2015
When we have large tree structure containing thousands of items, it may be hard to find all items that we are looking for. For this purpose, it is better if we could filter the hierarchy so that only those items that match our criteria are displayed.
IntegralUI TreeView directive for AngularJS includes an option to filter the tree hierarchy using multiple different conditions in and/or combinations. There are many string and numeric operators to choose from, or you can create your own custom filter operations.
Similar: TreeView Component for Angular 2
As it is presented in above demo, we are using string operations to filter our TreeView. In case of this example, three conditions are allowed in different combinations using AND/OR operators. Using more conditions is possible, but to keep this example simple, we will stick to just to three conditions.
Initially, TreeView is not filtered, which can also be shown by pressing the Reset button. There are three drop-down lists that contain all available string conditions:
Note These conditions are part of IntegralUI FIlter service, which is an AngularJS service, which includes all necessary methods to create filter operations. There are also numeric conditions and other kinds of filters, which we are not going to cover in this article.
If item value field is not set, then item's text is used in filtering, to match the provided value. In other cases where you have custom items with custom values, you may need to use different conditions.
To filter items, we can select one or multiple conditions. For each condition, we also must set the matching value against which items are compared. The value is case-sensitive, so we need to consider this during filtering.
For example, let say we want to show only items that ends with 'ts' or ends with 'es' and that does not contain the 'a' character. By selecting these conditions and clicking on Apply Filter button, teh TreeView will show only those items that match those conditions.
Behind the scene, the code looks like this:
angular
.module("appModule", ["integralui"])
.controller("appCtrl", ["$scope", "IntegralUITreeViewService", "$timeout", function($scope, $treeService, $timeout){
$scope.treeName = "treeSample";
$scope.items = [];
$scope.filterOptions = [ '', 'equals', 'does not equal', 'begins with', 'does not begin with', 'ends with', 'does not end with', 'contains', 'does not contain' ];
$scope.filterValues = [ '', '', '' ];
$scope.selOptions = [
$scope.filterOptions[0],
$scope.filterOptions[0],
$scope.filterOptions[0]
];
$scope.combinations = [ 'Or', 'Or' ];
var getOperation = function(option){
var index = $scope.filterOptions.indexOf(option);
switch (index){
case 1: //equals
return '=';
case 2: //does not equal
return '!=';
case 3: //begins with
return '->';
case 4: //does not begin with
return '->';
case 5: //ends with
return '<-';
case 6: //does not end with
return '<-';
case 7: //contains
return '[]';
case 8: //does not contain
return '[]';
}
return '';
}
var getFormula = function(conditions){
var formula = '';
var firstCombination = $scope.combinations[0];
var secondCombination = $scope.combinations[1];
if (conditions.length == 2 && $scope.selOptions[0] == '')
firstCombination = $scope.combinations[1];
var firstSymbol = conditions[0].negative ? '!a' : 'a';
var secondSymbol = conditions[1].negative ? '!b' : 'b';
if (firstCombination == 'And')
formula = '(' + firstSymbol + ' & ' + secondSymbol + ')';
else
formula = '(' + firstSymbol + ' | ' + secondSymbol + ')';
if (conditions.length == 3){
var thirdSymbol = conditions[2].negative ? '!c' : 'c';
if (secondCombination == 'And')
formula += ' & ' + thirdSymbol;
else
formula += ' | ' + thirdSymbol;
}
return formula;
}
}]);
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/integralui.css" />
<link rel="stylesheet" href="css/integralui.checkbox.css" />
<link rel="stylesheet" href="css/integralui.treeview.css" />
<link rel="stylesheet" href="css/themes/theme-flat-blue.css" />
<script type="text/javascript" src="external/angular.min.js"></script>
<script type="text/javascript" src="js/angular.integralui.min.js"></script>
<script type="text/javascript" src="js/angular.integralui.lists.min.js"></script>
<script type="text/javascript" src="js/angular.integralui.checkbox.min.js"></script>
<script type="text/javascript" src="js/angular.integralui.treeview.min.js"></script>
</head>
<body>
<div ng-app="appModule" ng-controller="appCtrl">
<iui-treeview name="{{treeName}}" items="items" show-check-boxes="true" item-checked-changed="onItemCheckedChanged(e)"></iui-treeview>
<div>
<label>Show items where: </label>
<div class="inline-block" style="margin-top:15px">
<select ng-model="selOptions[0]" ng-options="option for option in filterOptions"></select><input ng-model="filterValues[0]" type="text" style="width:125px"/>
</div>
<div class="inline-block" style="width:125px;margin:5px 15px">
<label class="inline-radio"><input type="radio" ng-model="combinations[0]" value="And" />And</label><label class="inline-radio"><input type="radio" ng-model="combinations[0]" value="Or" />Or</label>
</div>
<div class="inline-block">
<select ng-model="selOptions[1]" ng-options="option for option in filterOptions"></select><input ng-model="filterValues[1]" type="text" style="width:125px"/>
</div>
<div class="inline-block" style="width:125px;margin:5px 15px" align="center">
<label class="inline-radio"><input type="radio" ng-model="combinations[1]" value="And" />And</label><label class="inline-radio"><input type="radio" ng-model="combinations[1]" value="Or" />Or</label>
</div>
<div class="inline-block">
<select ng-model="selOptions[2]" ng-options="option for option in filterOptions"></select><input ng-model="filterValues[2]" type="text" style="width:125px"/>
</div>
<div class="inline-block" style="margin-top:25px;text-align:center;width:300px;">
<button ng-click="apply()">Apply Filter</button>
<button ng-click="reset()">Reset</button><br/><br/><br/>
<button ng-click="applyCustomFilter()" style="width:210px">Show Only Checked Items</button>
</div>
</div>
</div>
</body>
</html>
select
{
margin-right: 15px;
width: 150px;
}
.inline-radio
{
margin-right: 15px;
}
button
{
display: inline-block;
margin: 5px;
width: 100px;
height: 22px;
}
.inline-block
{
display: inline-block;
margin: 3px 0;
}
The getOperation function returns an operation symbol depending on which option is selected from drop-down list. When multiple options are selected, we need to create a formula by which conditions are combined. For this purpose getFormula function is used.
$scope.apply = function(){
var filterConditions = [];
for (var i = 0; i < 3; i++){
if ($scope.selOptions[i] != '' && $scope.filterValues[i] != ''){
var currentOperation = getOperation($scope.selOptions[i]);
filterConditions.push({
value: $scope.filterValues[i],
operation: currentOperation,
negative: $scope.filterOptions.indexOf($scope.selOptions[i]) % 2 == 0
});
}
}
if (filterConditions.length > 0){
var params = {}
if (filterConditions.length == 1){
params.conditions = filterConditions[0];
}
else {
params.conditions = filterConditions;
params.formula = getFormula(filterConditions);
}
$treeService.filter($scope.treeName, params);
}
else
$scope.reset();
}
$scope.reset = function(){
$treeService.filter($scope.treeName);
}
Next, when Apply Filter button is clicked, the apply function is called which creates filtering parameters using selected conditions and combinations. Then this parameters are added as argument to the filter method, which will process the filtering of the TreeView content.
At the end, if we are not happy with the result, we can press the Reset button, which also calls the filter method, but now without parameters. This will bring back the original content to the TreeView, showing all items.
Although the basic filtering operations are used in most cases, there may be some case where we need to create our own custom filter. This option is supported; we only need to provide a comparer function, which will be executed instead on the built-in functions.
In our example, we are going to create a filter that will show only checked items. For this purpose, we will use the value of item's checked field. In our comparer function instead of setting conditions (like in previous case), we are going to set only the callback function:
$scope.applyCustomFilter = function(){
var params = {
callback: function(value, item){
return item.checked;
}
}
$treeService.filter($scope.treeName, params);
}
In this function, the value argument is the item's value field. In our case, we will use item's checked field that determines whether the item is checked or not. By checking whether this value equals true, the item will pass the filter conditions and it is displayed in the TreeView. Other items that are not checked are excluded from the TreeView. You can also use any custom field applied to the item object, depending on the requirements of your app.
Note If root item does not pass the filter matching conditions, it is excluded from the TreeView along with all child items. Also, worth to mention here is that original tree structure in our app controller is not modified, the TreeView creates a temporary tree list of all items that pass the filtering conditions.
This sample with complete source code is available as part of the IntegralUI Studio for Web product package.