Angular Tree

by heavyhorse

HTML

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script type="text/ng-template" id="tree_item_renderer.html">
    <span ng-class="'depth-'+node.depth" ng-click="expand($event)">
        <pre ng-show="node.nodes.length > 0">&#x25B2;</pre>
        <pre ng-show="node.nodes.length == 0"> </pre>
        {{ node.name }}
    </span>
    <ul class="subtree">
        <li ng-repeat="node in node.nodes track by $index" ng-include="'tree_item_renderer.html'"></li>
    </ul>
</script>

<ul ng-controller="TreeController">
    <!-- <button ng-click="walk(tree, 0)">Walk</button> -->
    <li ng-repeat="node in tree track by $index" ng-include="'tree_item_renderer.html'"></li>
</ul>

CSS

* {
    -webkit-user-select: none;
    user-select: none;
}

ul {
    list-style: circle;
}

li {
    font-family: monospace;
    cursor: pointer;
}

span {
    padding: 2px;
    display: block;
    border: 1px solid rgba(0,0,0,0);
}

span pre {
    display: inline-block;
}

span:hover {
    background: #7fdbff;
    color: black;
}
span:focus, span:active, .active {
    border: 1px solid #001f3f;
    background: #001f3f;
    color: #80BFFF;
}
.depth-0 { padding-left: 15px; }
.depth-1 { padding-left: 30px; }
.depth-2 { padding-left: 45px; }
.depth-3 { padding-left: 60px; }
.depth-4 { padding-left: 75px; }
.depth-5 { padding-left: 90px; }
.depth-6 { padding-left: 105px; }
.depth-7 { padding-left: 16px; }
.depth-8 { padding-left: 18px; }
.depth-9 { padding-left: 20px; }

JavaScript

angular.module('app', [])
.controller('TreeController', function($scope, $timeout) {
    $scope.tree = [
        { name: 'root', nodes: [], depth: -1 }
    ];
    
    var treeDepth = 5;
    function generate(node, depth) {
        if(depth <= treeDepth) {
            var maxNodes = _.random(1, 5); 
            _(maxNodes).times(function(i) {
                node.nodes.push({name: 'node @ ('+depth+','+i+')', nodes: [], depth: depth });
                generate(node.nodes[i], depth+1);
            });
        } else {
            return;
        }
    }
    
    generate($scope.tree[0], 0);
    
    $timeout(function() {
        $scope.$apply();
    }, 500);
    
    $scope.add = function(node) {
        node.nodes.push({ name: 'node', nodes: [] });
    };
    
    $scope.expand = function($event) {
        console.log($event);
        angular.element('span').removeClass('active');
        angular.element($event.target).addClass('active');
    };
    
})
;