a suite of UI Components for development of web apps
Advanced User Interface Controls and Components
Created: 10 July 2015
Updated: 02 June 2016
AngularJS Tree Grid comes with option to add a check box to its column cells. By default these check boxes are not related, and whenever their value changes, the parent and child rows which contains a check box will not update its value. We will show you how to create this functionality, which will auto-update values of cascading checkboxes for all rows.
At first, we need to create a grid structure that displays a check box in first column for each row. In order for cascading checkbox changes to work, we need to set checkboxes to accept tri-state values. In this mode, when check box is clicked, it will change its value to:
Demonstration above shows check boxes in the first column of Tree Grid. Initially, some of these check boxes are 'checked', and their parent rows update their check box value to 'indeterminate'.
Whenever a check box is clicked, the cellClick event is fired. By handling this event, we can add operations that will update the values of check boxes in parent and child cells. To handle this event is simple; we need to create a function in our app code and add its signature to the cell-click attribute, like this:
angular
.module("appModule", ["integralui"])
.controller("appCtrl", ["$scope", "IntegralUITreeGridService", "$timeout", function($scope, $gridService, $timeout){
$scope.onCellClick = function(e){
if (e.cell && e.cell.cid == 1){
var checkValue = e.cell.value;
switch (checkValue){
case 'unchecked':
// Check box can become set to 'indeterminate' state only when some child rows are unchecked while others are checked.
checkValue = 'checked';
break;
case 'indeterminate':
checkValue = 'checked';
break;
case 'checked':
checkValue = 'unchecked';
break;
}
updateCheckValues(e.row, checkValue);
}
}
}]);
<!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.treegrid.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.treegrid.min.js"></script>
</head>
<body>
<div ng-app="appModule" ng-controller="appCtrl">
<iui-treegrid name="{{gridName}}" columns="columns" rows="rows" control-style="controlStyle" show-footer="false" cell-click="onCellClick(e)"></iui-treegrid>
</div>
</body>
</html>
.iui-treegrid-column-header-cell, .iui-treegrid-column-footer-cell
{
padding: 2px 2px;
}
.iui-treegrid-row-cell-content
{
padding: 2px 2px;
}
.row-hovered
{
background-color: #efefef;
}
.row-cell-hovered
{
color: #000080;
}
.row-selected
{
background-color: #dedede;
color: #008000;
}
.checkbox
{
width: 16px;
margin: auto;
}
.checkbox-box
{
border: 0;
display: inline-block;
width: 16px;
height: 16px;
}
.checkbox-checked
{
background-image: url("../../../resources/checkbox/checkbox-checked-7.png");
}
.checkbox-indeterminate
{
background-image: url("../../../resources/checkbox/checkbox-indeterminate-7.png");
}
.checkbox-unchecked
{
background-image: url("../../../resources/checkbox/checkbox-unchecked-7.png");
}
Because the cellClick event is fired for any clicked cell, we need to make sure that our operation is executed only for cells that belong to the first column. We can determine this by checking the cell cid field value. If it matches the value of our column with check boxes, the operation will be executed.
var getCheckCell = function(row){
var cell = null;
if (row && row.cells){
for (var j = 0; j < row.cells.length; j++){
if (row.cells[j].cid == 1){
cell = row.cells[j];
break;
}
}
}
return cell;
}
The cellClick event is fired before cell value is changed. Because of this, although the check box state is changed, the cell still has its old value. To handle this, we can add code which determines the correct state of cell value.
Now that we have this set up, we can create functions that will update the check boxes for cells in child and parent rows. For child rows, we will create a recursive functions that will execute for all child rows and update their checkbox value:
// Update the checkbox of child rows
var updateChildRowCheckValue = function(row, parentValue){
var cell = getCheckCell(row);
if (cell){
// Current Row
cell.value = parentValue;
// Child rows
var list = row.rows;
if (list && list.length > 0){
for (var i = 0; i < list.length; i++){
updateChildRowCheckValue(list[i], cell.value);
}
}
}
}
In similar way, we will create functions that will change the value of checkboxes in all parent rows for specified row. Depending on how many checkboxes in child rows are checked, undetermined or unchecked, the corresponding parent check box will changes its value accordingly.
// Update the checkbox of parent rows
var updateParentRowCheckValue = function(parent){
while (parent){
var parentCell = getCheckCell(parent);
if (parentCell){
var list = parent.rows;
if (list){
var checkCount = 0;
var indeterminateCount = 0;
for (var i = 0; i < list.length; i++){
var cell = getCheckCell(list[i]);
if (cell){
if (cell.value == 'checked')
checkCount++;
else if (cell.value == 'indeterminate')
indeterminateCount++;
}
}
if (checkCount == list.length)
parentCell.value = 'checked';
else if (checkCount > 0 || indeterminateCount > 0)
parentCell.value = 'indeterminate';
else
parentCell.value = 'unchecked';
}
}
parent = $gridService.getRowParent($scope.gridName, parent);
}
}
Finally, we can add the last function to the cellClick event, which will update checkbox cells in all parent and child rows:
var updateCheckValues = function(row, value){
// Child rows
updateChildRowCheckValue(row, value);
// Parent rows
var parent = $gridService.getRowParent($scope.gridName, row);
if (parent)
updateParentRowCheckValue(parent);
}
As it can be seen from above demo, whenever a check box is clicked, related parent and child checkboxes also update their value.