<!DOCTYPE html>
<html ng-app="angularjs-starter">
  
  <head lang="en">
    <meta charset="utf-8">
    <title>Custom Plunker</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.0.4/angular.min.js"></script>
    <link rel="stylesheet" href="style.css">
    <script>
      document.write('<base href="' + document.location + '" />');
    </script>
    <script src="app.js"></script>
  </head>
  
  <body ng-controller="MainCtrl">
  
    <ng-form>
    <input ng-model="node.name">
    <button ng-click="createNode(node.name)">create</button>
    </ng-form>
  
    <ul tree roots="nodes" item="n">
      <li draggable="true" ng-init="collapsed=false">
        <div class="node" ng-style="{'padding-left':(20*level)+'px'}">
          <button ng-click="collapsed=!collapsed">+</button>
          {{n.name}}
        </div>
        <div ng-show="!collapsed"><recur></recur></div>
      </li>
    </ul>
  
  </body>

</html>
var app = angular.module('angularjs-starter', []);

app.controller('MainCtrl', function($scope) {
    $scope.nodes = [
        {name: "Foo", children: [{name: "Bar", children: []}]},
        {name: "Lorem", children: [
            {name: "Ipsum", children: [{name: "Dolor", children: []}]}]},
        {name: "Quux", children: []}
    ];
    $scope.createNode = function(name) {
        $scope.nodes.push({name: name, children: []});
    };
});

app.directive('tree', function() {
    function linker(parent) {
        return function(clone) {
            parent.append(clone);
        };
    }
    
    function buildTree(parNode, level, nodes, scope, ident, transclude) {
        for (var i = 0; i < nodes.length; ++i) {
            var node = nodes[i];
            
            var nodeScope = scope.$new();
            nodeScope[ident] = node;
            nodeScope.level = level;
    
            transclude(nodeScope, linker(parNode));
            
            if (node.children.length > 0) {
                var ul = angular.element('<ul>');
                var children = parNode.children();
                var lastChild = angular.element(children[children.length - 1]);
                lastChild.find('recur').replaceWith(ul);
                buildTree(ul, level + 1, node.children, nodeScope, ident, transclude);
            }
        }
    }
    
    return {
        restrict: 'A',
        transclude: true,
        terminal: true,
        priority: 2000,
        compile: function(elem, attr, transclude) {
            return function(scope, elem, attr) {
                var nodes = scope.$eval(attr.roots);
                scope.$watch(attr.roots, function(o,n) {
                    elem.children().remove();
                    buildTree(elem, 0, nodes, scope, attr.item, transclude);
                    elem.find('recur').remove();
                }, true);
            };
        }
    };
});

/* CSS goes here */

ul { list-style:none; margin:0; padding:0; }
.node:hover { background-color:#f0f0f0; }