Recursive Tree
http://angularjs.org/
by Mohit Kumar Gupta
HTML
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css">
<script src="http://codeorigin.jquery.com/jquery-1.10.2.min.js"></script>
<script src="http://code.angularjs.org/angular-1.0.1.js"></script>
<div ng-controller="MyCtrl">
<ui-tree ng-model="assets" load-fn="loadChildren" expand-to="hierarchy" selected-id="111" attr-node-id="assetId"></ui-tree>
<div>selected: {{ selected.name }}</div>
</div>
CSS
/* tree */
.uiTree {
list-style-type: none;
margin-left: 10px;
padding-bottom: 5px;
}
.uiTree li a.icon {
display: inline-block;
width: 24px;
cursor: pointer;
vertical-align: text-bottom;
}
.uiTree li .node:hover > a.addChild {
display: inline-block;
}
.uiTree li a.addChild {
margin-left: 10px;
cursor: pointer;
display: none;
vertical-align: text-bottom;
}
.uiTree li .node {
padding-bottom: 5px;
}
.uiTree span.nodeLabel {
cursor: pointer;
}
.uiTree span.nodeLabel.selected {
border: 1px solid #aaa;
background-color: #ddd;
padding: 1px 3px;
}
JavaScript
var myApp = angular.module('myApp',[]);
myApp.directive('uiTree', function() {
return {
template: '<ul class="uiTree"><ui-tree-node ng-repeat="node in tree"></ui-tree-node></ul>',
replace: true,
transclude: true,
restrict: 'E',
scope: {
tree: '=ngModel',
attrNodeId: "@",
loadFn: '=',
expandTo: '=',
selectedId: '='
},
controller: function($scope, $element, $attrs) {
$scope.loadFnName = $attrs.loadFn;
// this seems like an egregious hack, but it is necessary for recursively-generated
// trees to have access to the loader function
if($scope.$parent.loadFn)
$scope.loadFn = $scope.$parent.loadFn;
// TODO expandTo shouldn't be two-way, currently we're copying it
if($scope.expandTo && $scope.expandTo.length) {
$scope.expansionNodes = angular.copy($scope.expandTo);
var arrExpandTo = $scope.expansionNodes.split(",");
$scope.nextExpandTo = arrExpandTo.shift();
$scope.expansionNodes = arrExpandTo.join(",");
}
}
};
})
.directive('uiTreeNode', ['$compile', '$timeout', function($compile, $timeout) {
return {
restrict: 'E',
replace: true,
template: '<li>' +
'<div class="node" data-node-id="{{ nodeId() }}">' +
'<a class="icon" ng-click="toggleNode(nodeId())""></a>' +
'<a ng-hide="selectedId" ng-href="#/assets/{{ nodeId() }}">{{ node.name }}</a>' +
'<span ng-show="selectedId" ng-class="css()" ng-click="setSelected(node)">' +
'{{ node.name }}</span>' +
'</div>' +
'</li>',
link: function(scope, elm, attrs) {
scope.nodeId = function(node) {
var localNode = node || scope.node;
return localNode[scope.attrNodeId];
};
scope.toggleNode = function(nodeId) {
var isVisible = elm.children(".uiTree:visible").length > 0;
var childrenTree = elm.children(".uiTree");
if(isVisible) {
...