Tuesday, October 11, 2016

Angular Tree View

For the second (or maybe third) time I faced a situation that would drastically benefit from a Tree View control.  Since I love Angular and was using it anyway, I finally set out to create a Tree View control I could reuse and be proud of.  It's pretty basic, but that's kinda the point.  There's actually a little bit of styling on this one, too.  Once again I'm going to try to get it up on Github at some point, but for now you can check it out on plnkr.

UPDATE: I've changed the directive significantly, checked it into Github and also created an npm package out of it called angular-tree-view.  I'm still updating a lot of it, but it's alive and in the wild now!

Check it out on Github
Check it out on npm

An important piece to note is that this feature actually uses two different directives nested within each other.  There's the parent item, which is a tree-view and then there's the child item, which is a tree-view-item.  Each tree-view-item can have another tree-view in it, which can then have another tree-view-item, which can then have another tree-view, etc.

Directive Requirements:
  • Each item can have an unlimited number of sub-items
  • Each sub-item can have an unlimited number of sub-items (ad infinitum)
  • The user can specify a callback to be invoked when a sub-item with no sub-items of its own is clicked
  • The selected item should be indicated by the application of an "active-branch" class
Directive Module Code:
angular.module('ea.treeview', []).directive('eaTreeView', function() {
  return {
        restrict: 'E',
        scope: {
            items: '=',
            isRoot: '@',
            callback: '&'
        },
        link: function(scope) {
            scope.callback = scope.callback();
        },
        controller: function ($scope) {
            this.isRoot = $scope.isRoot;
        },
        template: '<ea-tree-view-item item="item" callback="callback" data-ng-repeat="item in items"></ea-tree-view-item>'
    }
}).directive('eaTreeViewItem', function() {
  return {
        restrict: 'E',
        scope: {
            item: '=',
            callback: '&'
        },
        require: '^eaTreeView',
        link: function (scope, element, attributes, controller) {
          scope.callback = scope.callback();
            scope.activate = function () {
              var activeElements = document.getElementsByClassName('active-branch');
              angular.forEach(activeElements, function(activeElement){
                angular.element(activeElement).removeClass('active-branch');
              });
              element.children('div').addClass('active-branch');
              scope.callback(scope.item);
            };

            scope.hasChildren = function () {
                return !!scope.item.items && !!scope.item.items.length;
            };

            scope.hasParent = function() {
                return !controller.isRoot;
            };

            scope.toggleExpanded = function() {
                scope.item.expanded = !scope.item.expanded;
            };
        },
        templateUrl: 'treeViewItem.html'
    }
});

The treeViewItem Template:
<div>
    <div data-ng-if="hasChildren()" class="tree-parent" data-ng-class="{'tree-child': hasParent()}">
        <div data-ng-click="toggleExpanded()" class="clickable">
            <i class="fa" data-ng-class="{'fa-chevron-right': !item.expanded, 'fa-chevron-down': item.expanded}"></i>
            <span>{{item.display}}</span>
        </div>
        <ea-tree-view data-ng-if="item.expanded" callback="callback" items="item.items"></ea-tree-view>
    </div>
    <div data-ng-if="!hasChildren()" data-ng-click="activate()" class="tree-child clickable">{{item.display}}</div>
</div>

A dash of styling:
.clickable {
  cursor: pointer;
}

.active-branch {
    background-color: lightgrey;
}

.tree-parent {
  padding-left: 0;
}

.tree-child {
  padding-left: 1em;
}

.tree-final-child {
  padding-left: 2em;
}

And finally some usage:
<ea-tree-view items="model.items" is-root="true" callback="go"></ea-tree-view>

angular.module('demo', ['ea.treeview']).controller('mainController', function($scope) {
  $scope.model = {
    items: [
      {
        display: 'Abe',
        items: [
          {display: 'Homer', items: [{display: 'Bart'}, {display: 'Lisa'}, {display: 'Maggie'}]},
          {display: 'Herb'},
          {display: 'Abbie'}
        ]
      },
      {
        display: 'Jacqueline',
        items: [
          {display: 'Patty'},
          {display: 'Selma'},
          {display: 'Marge', items: [{display: 'Bart'}, {display: 'Lisa'}, {display: 'Maggie'}]}
        ]
      }
    ]
  };
  
  $scope.go = function (item) {
      console.log("You could have navigated!", item);
  };
});

1 comment:

  1. Answers I Couldn'T Find Anywhere Else: Angular Tree View >>>>> Download Now

    >>>>> Download Full

    Answers I Couldn'T Find Anywhere Else: Angular Tree View >>>>> Download LINK

    >>>>> Download Now

    Answers I Couldn'T Find Anywhere Else: Angular Tree View >>>>> Download Full

    >>>>> Download LINK xB

    ReplyDelete