Angular Tree Table With Checkboxes

Angular Tree Table With Checkboxes
Project: Tree Table and Checkbox with AngularJS
Author: naiting
Edit Online: View on CodePen
License: MIT

Check out this Angular tree table with the checkboxes code snippet. It provides a hierarchical tree structure with checkboxes for each item. The code allows you to toggle the selection of individual items and also provides an option to select/deselect all items at once.

The tree table consists of different categories each containing multiple nested items. You can easily integrate this code into your Angular application to implement a tree table with checkboxes for efficient data selection.

How to Create a Tree Table With Checkboxes

First of all, load the following assets into the head tag of your HTML document.

<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css'>

Create the HTML structure for tree table as follows:

<div class="wrapper" ng-app="testApp" ng-controller="treeTable">
  <table class="table-nested">
    <thead>
      <tr>
        <th class="cell-input">
          <input ng-checked="(list | selected).length == list.length" ng-click="toggleAllCheckboxes($event)" type="checkbox" />
        </th>
        <th>
          Name
        </th>
        <th class="cell-members">
          Members
        </th>
        <th>
          Title
        </th>
      </tr>
    </thead>
    <tbody ng-class="{opened: item.opened}" ng-include="&#39;table_tree.html&#39;" ng-repeat="item in list"></tbody>
  </table>
  <script id="table_tree.html" type="text/ng-template">
    <tr ng-class="{parent: item.children}" ng-init="parentScope = $parent.$parent; initCheckbox(item, parentScope.item)">
      <td class="cell-input">
        <input ng-change="toggleCheckbox(item, parentScope)" ng-model="item.selected" type="checkbox" />
      </td>
      <td class="cell-name" ng-click="item.opened = !item.opened">
        <div class="indent" style="padding-left: {{15*level}}px"></div>
        {{item.name}}
      </td>
      <td class="cell-members">
        {{item.children.length}}
      </td>
      <td>
        {{item.title}}
      </td>
    </tr>
    <tr class="children" ng-if="item.children &amp;&amp; item.children.length &gt; 0">
      <td colspan="4">
        <table>
          <tbody ng-class="{opened: item.opened}" ng-include="&#39;table_tree.html&#39;" ng-init="level = level + 1" ng-repeat="item in item.children"></tbody>
        </table>
      </td>
    </tr>
  </script>
</div>

Style the table using the following CSS styles. These styles are optional, you can define your own CSS according to your needs.

@charset "UTF-8";
body {
  font: 13px helvetica;
  width: 80%;
  margin: 40px auto;
  background: #eee;
  text-align: center;
}

table {
  width: 100%;
  table-layout: fixed;
  border-collapse: collapse;
  border-spacing: 0;
}

.table-nested {
  background: #fff;
  border: 2px solid #444;
  text-align: left;
}
.table-nested th, .table-nested td {
  padding: 0;
}
.table-nested th + th, .table-nested th + td, .table-nested td + th, .table-nested td + td {
  padding-left: 5px;
}
.table-nested td {
  border-top: 1px solid;
}
.table-nested td[colspan] {
  border: none;
}
.table-nested .cell-input {
  width: 20px;
  border-right: 1px solid;
}
.table-nested .cell-members {
  width: 100px;
}
.table-nested .indent {
  display: inline-block;
}
.table-nested .parent > .cell-name {
  cursor: pointer;
}
.table-nested .parent > .cell-name > .indent {
  margin-right: 5px;
}
.table-nested .parent > .cell-name > .indent:before {
  content: "";
  font-family: FontAwesome;
  display: inline-block;
  -moz-transition: -moz-transform 0.3s;
  -o-transition: -o-transform 0.3s;
  -webkit-transition: -webkit-transform 0.3s;
  transition: transform 0.3s;
}
.table-nested .children {
  display: none;
}
.table-nested .opened > tr > .cell-name > .indent:before {
  -moz-transform: rotate(90deg);
  -ms-transform: rotate(90deg);
  -webkit-transform: rotate(90deg);
  transform: rotate(90deg);
}
.table-nested .opened > .children {
  display: table-row;
}

Now, make sure you have loaded AngularJS into your project. You can load the AngularJS by adding the following CDN link before closing the body tag:

<script src='//ajax.googleapis.com/ajax/libs/angularjs/1.3.2/angular.min.js'></script>

Finally, add the following JavaScript function to active the tree table:

(function() {
  var app, list;

  list = [
    {
      name: 'Developer',
      opened: true,
      children: [
        {
          name: 'Front-End',
          children: [
            {
              name: 'Jack',
              title: 'Leader'
            },
            {
              name: 'John',
              title: 'Senior F2E'
            },
            {
              name: 'Jason',
              title: 'Junior F2E'
            }
          ]
        },
        {
          name: 'Back-End',
          children: [
            {
              name: 'Mary',
              title: 'Leader'
            },
            {
              name: 'Gary',
              title: 'Intern'
            }
          ]
        }
      ]
    },
    {
      name: 'Design',
      children: [
        {
          name: 'Freeman',
          title: 'Designer'
        }
      ]
    },
    {
      name: 'S&S',
      children: [
        {
          name: 'Nikky',
          title: 'Robot'
        }
      ]
    }
  ];

  app = angular.module('testApp', []).controller('treeTable', [
    '$scope',
    '$filter',
    function($scope,
    $filter) {
      $scope.list = list;
      $scope.toggleAllCheckboxes = function($event) {
        var i,
    item,
    len,
    ref,
    results,
    selected;
        selected = $event.target.checked;
        ref = $scope.list;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          item = ref[i];
          item.selected = selected;
          if (item.children != null) {
            results.push($scope.$broadcast('changeChildren',
    item));
          } else {
            results.push(void 0);
          }
        }
        return results;
      };
      $scope.initCheckbox = function(item,
    parentItem) {
        return item.selected = parentItem && parentItem.selected || item.selected || false;
      };
      $scope.toggleCheckbox = function(item,
    parentScope) {
        if (item.children != null) {
          $scope.$broadcast('changeChildren',
    item);
        }
        if (parentScope.item != null) {
          return $scope.$emit('changeParent',
    parentScope);
        }
      };
      $scope.$on('changeChildren',
    function(event,
    parentItem) {
        var child,
    i,
    len,
    ref,
    results;
        ref = parentItem.children;
        results = [];
        for (i = 0, len = ref.length; i < len; i++) {
          child = ref[i];
          child.selected = parentItem.selected;
          if (child.children != null) {
            results.push($scope.$broadcast('changeChildren',
    child));
          } else {
            results.push(void 0);
          }
        }
        return results;
      });
      return $scope.$on('changeParent',
    function(event,
    parentScope) {
        var children;
        children = parentScope.item.children;
        parentScope.item.selected = $filter('selected')(children).length === children.length;
        parentScope = parentScope.$parent.$parent;
        if (parentScope.item != null) {
          return $scope.$broadcast('changeParent',
    parentScope);
        }
      });
    }
  ]);

  app.filter('selected', [
    '$filter',
    function($filter) {
      return function(files) {
        return $filter('filter')(files,
    {
          selected: true
        });
      };
    }
  ]);

}).call(this);

//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiPGFub255bW91cz4iXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUFBQSxNQUFBLEdBQUEsRUFBQTs7RUFBQSxJQUFBLEdBQU87SUFDSDtNQUFBLElBQUEsRUFBTSxXQUFOO01BQ0EsTUFBQSxFQUFRLElBRFI7TUFFQSxRQUFBLEVBQVU7UUFDTjtVQUFBLElBQUEsRUFBTSxXQUFOO1VBQ0EsUUFBQSxFQUFVO1lBQ047Y0FBQSxJQUFBLEVBQU0sTUFBTjtjQUNBLEtBQUEsRUFBTztZQURQLENBRE07WUFJTjtjQUFBLElBQUEsRUFBTSxNQUFOO2NBQ0EsS0FBQSxFQUFPO1lBRFAsQ0FKTTtZQU9OO2NBQUEsSUFBQSxFQUFNLE9BQU47Y0FDQSxLQUFBLEVBQU87WUFEUCxDQVBNOztRQURWLENBRE07UUFhTjtVQUFBLElBQUEsRUFBTSxVQUFOO1VBQ0EsUUFBQSxFQUFVO1lBQ047Y0FBQSxJQUFBLEVBQU0sTUFBTjtjQUNBLEtBQUEsRUFBTztZQURQLENBRE07WUFJTjtjQUFBLElBQUEsRUFBTSxNQUFOO2NBQ0EsS0FBQSxFQUFPO1lBRFAsQ0FKTTs7UUFEVixDQWJNOztJQUZWLENBREc7SUEwQkg7TUFBQSxJQUFBLEVBQU0sUUFBTjtNQUNBLFFBQUEsRUFBVTtRQUNSO1VBQUEsSUFBQSxFQUFNLFNBQU47VUFDQSxLQUFBLEVBQU87UUFEUCxDQURROztJQURWLENBMUJHO0lBZ0NIO01BQUEsSUFBQSxFQUFNLEtBQU47TUFDQSxRQUFBLEVBQVU7UUFDTjtVQUFBLElBQUEsRUFBTSxPQUFOO1VBQ0EsS0FBQSxFQUFPO1FBRFAsQ0FETTs7SUFEVixDQWhDRzs7O0VBdUNQLEdBQUEsR0FBTSxPQUFPLENBQUMsTUFBUixDQUFlLFNBQWYsRUFBMEIsRUFBMUIsQ0FBNkIsQ0FBQyxVQUE5QixDQUF5QyxXQUF6QyxFQUFzRDtJQUFFLFFBQUY7SUFBWSxTQUFaO0lBQXVCLFFBQUEsQ0FBQyxNQUFEO0lBQVMsT0FBVCxDQUFBO01BQ2pGLE1BQU0sQ0FBQyxJQUFQLEdBQWM7TUFFZCxNQUFNLENBQUMsbUJBQVAsR0FBNkIsUUFBQSxDQUFDLE1BQUQsQ0FBQTtBQUMvQixZQUFBLENBQUE7SUFBQSxJQUFBO0lBQUEsR0FBQTtJQUFBLEdBQUE7SUFBQSxPQUFBO0lBQUE7UUFBSSxRQUFBLEdBQVcsTUFBTSxDQUFDLE1BQU0sQ0FBQztBQUN6QjtBQUFBO1FBQUEsS0FBQSxxQ0FBQTs7VUFDRSxJQUFJLENBQUMsUUFBTCxHQUFnQjtVQUNoQixJQUE2QyxxQkFBN0M7eUJBQUEsTUFBTSxDQUFDLFVBQVAsQ0FBa0IsZ0JBQWxCO0lBQW9DLElBQXBDLEdBQUE7V0FBQSxNQUFBO2lDQUFBOztRQUZGLENBQUE7O01BRjJCO01BTTdCLE1BQU0sQ0FBQyxZQUFQLEdBQXNCLFFBQUEsQ0FBQyxJQUFEO0lBQU8sVUFBUCxDQUFBO2VBQ3BCLElBQUksQ0FBQyxRQUFMLEdBQWdCLFVBQUEsSUFBYyxVQUFVLENBQUMsUUFBekIsSUFBcUMsSUFBSSxDQUFDLFFBQTFDLElBQXNEO01BRGxEO01BR3RCLE1BQU0sQ0FBQyxjQUFQLEdBQXdCLFFBQUEsQ0FBQyxJQUFEO0lBQU8sV0FBUCxDQUFBO1FBQ3RCLElBQTZDLHFCQUE3QztVQUFBLE1BQU0sQ0FBQyxVQUFQLENBQWtCLGdCQUFsQjtJQUFvQyxJQUFwQyxFQUFBOztRQUNBLElBQTZDLHdCQUE3QztpQkFBQSxNQUFNLENBQUMsS0FBUCxDQUFhLGNBQWI7SUFBNkIsV0FBN0IsRUFBQTs7TUFGc0I7TUFJeEIsTUFBTSxDQUFDLEdBQVAsQ0FBVyxnQkFBWDtJQUE2QixRQUFBLENBQUMsS0FBRDtJQUFRLFVBQVIsQ0FBQTtBQUMvQixZQUFBLEtBQUE7SUFBQSxDQUFBO0lBQUEsR0FBQTtJQUFBLEdBQUE7SUFBQTtBQUFJO0FBQUE7UUFBQSxLQUFBLHFDQUFBOztVQUNFLEtBQUssQ0FBQyxRQUFOLEdBQWlCLFVBQVUsQ0FBQztVQUM1QixJQUE4QyxzQkFBOUM7eUJBQUEsTUFBTSxDQUFDLFVBQVAsQ0FBa0IsZ0JBQWxCO0lBQW9DLEtBQXBDLEdBQUE7V0FBQSxNQUFBO2lDQUFBOztRQUZGLENBQUE7O01BRDJCLENBQTdCO2FBS0EsTUFBTSxDQUFDLEdBQVAsQ0FBVyxjQUFYO0lBQTJCLFFBQUEsQ0FBQyxLQUFEO0lBQVEsV0FBUixDQUFBO0FBQzdCLFlBQUE7UUFBSSxRQUFBLEdBQVcsV0FBVyxDQUFDLElBQUksQ0FBQztRQUM1QixXQUFXLENBQUMsSUFBSSxDQUFDLFFBQWpCLEdBQTRCLE9BQUEsQ0FBUSxVQUFSLENBQUEsQ0FBb0IsUUFBcEIsQ0FBNkIsQ0FBQyxNQUE5QixLQUF3QyxRQUFRLENBQUM7UUFDN0UsV0FBQSxHQUFjLFdBQVcsQ0FBQyxPQUFPLENBQUM7UUFDbEMsSUFBa0Qsd0JBQWxEO2lCQUFBLE1BQU0sQ0FBQyxVQUFQLENBQWtCLGNBQWxCO0lBQWtDLFdBQWxDLEVBQUE7O01BSnlCLENBQTNCO0lBckJpRixDQUF2QjtHQUF0RDs7RUE0Qk4sR0FBRyxDQUFDLE1BQUosQ0FBVyxVQUFYLEVBQXVCO0lBQUMsU0FBRDtJQUFZLFFBQUEsQ0FBQyxPQUFELENBQUE7YUFDakMsUUFBQSxDQUFDLEtBQUQsQ0FBQTtlQUNFLE9BQUEsQ0FBUSxRQUFSLENBQUEsQ0FBa0IsS0FBbEI7SUFBeUI7VUFBQyxRQUFBLEVBQVU7UUFBWCxDQUF6QjtNQURGO0lBRGlDLENBQVo7R0FBdkI7QUFuRUEiLCJzb3VyY2VzQ29udGVudCI6WyJsaXN0ID0gW1xuICAgIG5hbWU6ICdEZXZlbG9wZXInXG4gICAgb3BlbmVkOiB0cnVlXG4gICAgY2hpbGRyZW46IFtcbiAgICAgICAgbmFtZTogJ0Zyb250LUVuZCdcbiAgICAgICAgY2hpbGRyZW46IFtcbiAgICAgICAgICAgIG5hbWU6ICdKYWNrJ1xuICAgICAgICAgICAgdGl0bGU6ICdMZWFkZXInXG4gICAgICAgICAgLFxuICAgICAgICAgICAgbmFtZTogJ0pvaG4nXG4gICAgICAgICAgICB0aXRsZTogJ1NlbmlvciBGMkUnXG4gICAgICAgICAgLFxuICAgICAgICAgICAgbmFtZTogJ0phc29uJ1xuICAgICAgICAgICAgdGl0bGU6ICdKdW5pb3IgRjJFJ1xuICAgICAgICBdXG4gICAgICAsXG4gICAgICAgIG5hbWU6ICdCYWNrLUVuZCdcbiAgICAgICAgY2hpbGRyZW46IFtcbiAgICAgICAgICAgIG5hbWU6ICdNYXJ5JyxcbiAgICAgICAgICAgIHRpdGxlOiAnTGVhZGVyJ1xuICAgICAgICAgICxcbiAgICAgICAgICAgIG5hbWU6ICdHYXJ5J1xuICAgICAgICAgICAgdGl0bGU6ICdJbnRlcm4nXG4gICAgICAgIF1cbiAgICBdXG4gICxcbiAgICBuYW1lOiAnRGVzaWduJ1xuICAgIGNoaWxkcmVuOiBbXG4gICAgICBuYW1lOiAnRnJlZW1hbidcbiAgICAgIHRpdGxlOiAnRGVzaWduZXInXG4gICAgXVxuICAsXG4gICAgbmFtZTogJ1MmUydcbiAgICBjaGlsZHJlbjogW1xuICAgICAgICBuYW1lOiAnTmlra3knXG4gICAgICAgIHRpdGxlOiAnUm9ib3QnXG4gICAgXVxuXVxuXG5hcHAgPSBhbmd1bGFyLm1vZHVsZSgndGVzdEFwcCcsIFtdKS5jb250cm9sbGVyKCd0cmVlVGFibGUnLCBbICckc2NvcGUnLCAnJGZpbHRlcicsICgkc2NvcGUsICRmaWx0ZXIpIC0+XG4gICRzY29wZS5saXN0ID0gbGlzdCAgXG4gIFxuICAkc2NvcGUudG9nZ2xlQWxsQ2hlY2tib3hlcyA9ICgkZXZlbnQpIC0+XG4gICAgc2VsZWN0ZWQgPSAkZXZlbnQudGFyZ2V0LmNoZWNrZWRcbiAgICBmb3IgaXRlbSBpbiAkc2NvcGUubGlzdFxuICAgICAgaXRlbS5zZWxlY3RlZCA9IHNlbGVjdGVkXG4gICAgICAkc2NvcGUuJGJyb2FkY2FzdCgnY2hhbmdlQ2hpbGRyZW4nLCBpdGVtKSBpZiBpdGVtLmNoaWxkcmVuP1xuXG4gICRzY29wZS5pbml0Q2hlY2tib3ggPSAoaXRlbSwgcGFyZW50SXRlbSkgLT5cbiAgICBpdGVtLnNlbGVjdGVkID0gcGFyZW50SXRlbSAmJiBwYXJlbnRJdGVtLnNlbGVjdGVkIHx8IGl0ZW0uc2VsZWN0ZWQgfHwgZmFsc2VcbiAgXG4gICRzY29wZS50b2dnbGVDaGVja2JveCA9IChpdGVtLCBwYXJlbnRTY29wZSkgLT5cbiAgICAkc2NvcGUuJGJyb2FkY2FzdCgnY2hhbmdlQ2hpbGRyZW4nLCBpdGVtKSBpZiBpdGVtLmNoaWxkcmVuP1xuICAgICRzY29wZS4kZW1pdCgnY2hhbmdlUGFyZW50JywgcGFyZW50U2NvcGUpIGlmIHBhcmVudFNjb3BlLml0ZW0/XG5cbiAgJHNjb3BlLiRvbiAnY2hhbmdlQ2hpbGRyZW4nLCAoZXZlbnQsIHBhcmVudEl0ZW0pIC0+XG4gICAgZm9yIGNoaWxkIGluIHBhcmVudEl0ZW0uY2hpbGRyZW5cbiAgICAgIGNoaWxkLnNlbGVjdGVkID0gcGFyZW50SXRlbS5zZWxlY3RlZFxuICAgICAgJHNjb3BlLiRicm9hZGNhc3QoJ2NoYW5nZUNoaWxkcmVuJywgY2hpbGQpIGlmIGNoaWxkLmNoaWxkcmVuP1xuXG4gICRzY29wZS4kb24gJ2NoYW5nZVBhcmVudCcsIChldmVudCwgcGFyZW50U2NvcGUpIC0+XG4gICAgY2hpbGRyZW4gPSBwYXJlbnRTY29wZS5pdGVtLmNoaWxkcmVuXG4gICAgcGFyZW50U2NvcGUuaXRlbS5zZWxlY3RlZCA9ICRmaWx0ZXIoJ3NlbGVjdGVkJykoY2hpbGRyZW4pLmxlbmd0aCA9PSBjaGlsZHJlbi5sZW5ndGhcbiAgICBwYXJlbnRTY29wZSA9IHBhcmVudFNjb3BlLiRwYXJlbnQuJHBhcmVudFxuICAgICRzY29wZS4kYnJvYWRjYXN0KCdjaGFuZ2VQYXJlbnQnLCBwYXJlbnRTY29wZSkgaWYgcGFyZW50U2NvcGUuaXRlbT9cbl0pXG5cbmFwcC5maWx0ZXIgJ3NlbGVjdGVkJywgWyckZmlsdGVyJywgKCRmaWx0ZXIpIC0+XG4gIChmaWxlcykgLT5cbiAgICAkZmlsdGVyKCdmaWx0ZXInKShmaWxlcywge3NlbGVjdGVkOiB0cnVlfSlcbl0iXX0=
//# sourceURL=coffeescript

That’s all! hopefully, you have successfully integrated this AngularJS tree table with checkboxes into your project. If you have any questions or suggestions, feel free to comment below.

Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply