12

I am building a web app where I need to display a tree using lists. My basic structure looks like this:

* Node 1
    * Node 1.1
        * Node 1.1.1
            * Node 1.1.1.1
        * Node 1.1.2
    * Node 1.2

http://jsfiddle.net/QffFm/1/

I'm trying to find something in angular or bootstrap that I can use such that:

  • At first view of the list, it is expanded up to the third layer. In my fiddle, I would want to see Node 1, Node 1.1, Node 1.1.1, Node 1.1.2 and Node 1.2 (all but the 4th layer - Node 1.1.1.1)
  • On clicking on the list-style icon (not the word name of the node) The node collapses or expands
  • Ideally, I would love for the icon to change also dependent on if the item is expanded. A right arrow if there is more underneath, a down arrow if it is already expanded, and maybe a regular list item if there are no children

I am very new to AngularJS and still quite new to Bootstrap as well. I see that Angular has an accordion function which doesn't seem to quite handle everything I need it to.

I would love some direction on the best approach before I code a lot of logic into my web app that handles the different cases. I think this must be a common problem so perhaps there is something ready made that I can utilize. Any guidance would be much appreciated.

HTML code:

<div ng-app="myApp" ng-controller="controller">
    <my-directive></my-directive>
    <table style="width: 100%"><tbody><td>
        <tree items="tree"></tree>
    </td></tbody></table>
</div>

Angular code:

var app = angular.module('myApp', []);

app.controller('controller', function ($scope){ 

    $scope.tree=[{"name":"Node 1","items":[{"name":"Node 1.1","items":[{"name":"Node 1.1.1","items":[{"name":"Node 1.1.1.1","items":[]}]},{"name":"Node 1.1.2","items":[]}]},{"name":"Node 1.2","items":[]}]}];

});
app.directive('tree', function() {
    return {
        template: '<ul><tree-node ng-repeat="item in items"></tree-node></ul>',
        restrict: 'E',
        replace: true,
        scope: {
            items: '=items',
        }
    };
});

app.directive('treeNode', function($compile) {
    return { 
        restrict: 'E',
        template: '<li >{{item.name}}</li>',
        link: function(scope, elm, attrs) {
        if (scope.item.items.length > 0) {
            var children = $compile('<tree items="item.items"></tree>')(scope);
            elm.append(children);
        }
    }
    };
});

1 Answer 1

32

In followed example I used:

  • bootstrap
  • AngularJS recursive ng-include or (see second example) recursive directives
  • jQuery (will try to avoid in the future)

Demo 1 (ng-include) Plunker

enter image description here

From this model:

 $scope.displayTree =
            [{
            "name": "Root",
            "type_name": "Node",
            "show": true,
            "nodes": [{
                "name": "Loose",
                "group_name": "Node-1",
                "show": true,
                "nodes": [{
                    "name": "Node-1-1",
                    "device_name": "Node-1-1",
                    "show": true,
                    "nodes": []
                }, {
                    "name": "Node-1-2",
                    "device_name": "Node-1-2",
                    "show": true,
                    "nodes": []
                }, {
                    "name": "Node-1-3",
                    "device_name": "Node-1-3",
                    "show": true,
                    "nodes": []
                }]
            }, {
                "name": "God",
                "group_name": "Node-2",
                "show": true,
                "nodes": [{
                    "name": "Vadar",
                    "device_name": "Node-2-1",
                    "show": true,
                    "nodes": []
                }]
            }, {
                "name": "Borg",
                "group_name": "Node-3",
                "show": true,
                "nodes": []
            }, {
                "name": "Fess",
                "group_name": "Node-4",
                "show": true,
                "nodes": []
            }]
        }];
        [{
            "name": "Android",
            "type_name": "Android",
            "icon": "icon-android icon-3",
            "show": true,
            "nodes": []
        }];
    }

The 2nd example is based on 2 directives:

app.directive('nodeTree', function() {
      return {
        template: '<node ng-repeat="node in tree"></node>',
        replace: true,
        transclude: true,
        restrict: 'E',
        scope: {
          tree: '=ngModel'
        }
      };
});

app.directive('node', function($compile) {
  return { 
    restrict: 'E',
    replace:true,
     templateUrl: 'the-tree.html',
    link: function(scope, elm, attrs) {
    
      // ....     
     
      if (scope.node.children.length > 0) {
        var childNode = $compile('<ul ><node-tree ng-model="node.children"></node-tree></ul>')(scope)
        elm.append(childNode);
      }
    }
  };
}); 

(Added some checkboxes as well :))

Demo 2 Plunker

How it looks:

enter image description here

Sign up to request clarification or add additional context in comments.

4 Comments

@im1dermike right, resource URL changed but you can change and make it work. I'll update both demos shortly. Thanks
@MaximShoustin Is it possible to convert this horizontal. I changed the css to this but its not that great as you can see here
@jackyrudetsky I suggest you to open new question with this problem and please post here link for reference. Thanks
@jackyrudetsky add $('.tree ul:not(:has(*))').css('display', 'none'). Link: plnkr.co/edit/4623L8LTmOyPPekBUyrh?p=preview

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.