是否有可能使用Angular进行树视图?

我正在寻找在Web应用程序中显示树形结构中的数据。 我希望使用Angular来完成这个任务。

看起来像ng-repeat将允许我遍历节点列表,但是如何在给定节点的深度增加时进行嵌套?

我尝试了下面的代码 ,但HTML的自动转义正在阻止这个工作。 另外,结束ul标签是在错误的地方。

我很确定,我正在以完全错误的方式解决这个问题。

有任何想法吗?

看看这个小提琴http://jsfiddle.net/brendanowen/uXbn6/8/

这应该给你一个好主意,如何使用angular度来显示tree like structure 。 这是一种在HTML中使用recursion!

如果您正在使用Bootstrap CSS …

我已经创build了一个基于Bootstrap“nav”列表的AngularJS的简单的可重用的树形控件(指令)。 我添加了额外的缩进,图标和animation。 HTML属性用于configuration。

它不使用recursion。

我把它称为angular度引导 – 导航树 (吸引人的名字,你不觉得吗?)

这里有一个例子,来源在这里 。

当做这样的事情时,最好的解决scheme是recursion指令。 然而,当你做出这样的指示时,你会发现AngularJS陷入了一个无限循环。

解决方法是让指令在编译事件中删除元素,并手动编译并将它们添加到链接事件中。

我在这个线程中发现了这个,并将这个function抽象成一个服务 。

 module.factory('RecursionHelper', ['$compile', function($compile){ return { /** * Manually compiles the element, fixing the recursion loop. * @param element * @param [link] A post-link function, or an object with function(s) registered via pre and post properties. * @returns An object containing the linking functions. */ compile: function(element, link){ // Normalize the link parameter if(angular.isFunction(link)){ link = { post: link }; } // Break the recursion loop by removing the contents var contents = element.contents().remove(); var compiledContents; return { pre: (link && link.pre) ? link.pre : null, /** * Compiles and re-adds the contents */ post: function(scope, element){ // Compile the contents if(!compiledContents){ compiledContents = $compile(contents); } // Re-add the compiled contents to the element compiledContents(scope, function(clone){ element.append(clone); }); // Call the post-linking function, if any if(link && link.post){ link.post.apply(null, arguments); } } }; } }; }]); 

有了这个服务,你可以很容易地做一个树指令(或其他recursion指令)。 这是一个树指令的例子:

 module.directive("tree", function(RecursionHelper) { return { restrict: "E", scope: {family: '='}, template: '<p>{{ family.name }}</p>'+ '<ul>' + '<li ng-repeat="child in family.children">' + '<tree family="child"></tree>' + '</li>' + '</ul>', compile: function(element) { return RecursionHelper.compile(element); } }; }); 

看到这个Plunker的演示。 我最喜欢这个解决scheme,因为:

  1. 你不需要一个特殊的指令,使你的html不那么干净。
  2. recursion逻辑被抽象为RecursionHelper服务,所以你保持你的指令干净。

更新:增加了对自定义链接function的支持。

有棱angular的树似乎为我做了一个好工作

以下是使用recursion指令的示例: http : //jsfiddle.net/n8dPm/取自https://groups.google.com/forum/#!topic/angular/vswXTes_FtM

 module.directive("tree", function($compile) { return { restrict: "E", scope: {family: '='}, template: '<p>{{ family.name }}</p>'+ '<ul>' + '<li ng-repeat="child in family.children">' + '<tree family="child"></tree>' + '</li>' + '</ul>', compile: function(tElement, tAttr) { var contents = tElement.contents().remove(); var compiledContents; return function(scope, iElement, iAttr) { if(!compiledContents) { compiledContents = $compile(contents); } compiledContents(scope, function(clone, scope) { iElement.append(clone); }); }; } }; }); 

这一个似乎更完整: https : //github.com/dump247/angular.tree

另一个基于原始源代码的示例,已经有一个示例树结构(更容易看到它是如何工作的IMO)和一个filter来search树:

的jsfiddle

这么多很棒的解决scheme,但是我觉得他们都是这样或者那样一些过分复杂的东西。

我想创build一些重新创build@Mark Lagendijk的awnser的简单的东西,但没有它在指令中定义模板,而是让“用户”在HTML中创build模板…

https://github.com/stackfull/angular-tree-repeat等取得的想法;…我最终创build的项目: https : //github.com/dotJEM/angular-tree

它允许你build立你的树,如:

 <ul dx-start-with="rootNode"> <li ng-repeat="node in $dxPrior.nodes"> {{ node.name }} <ul dx-connect="node"/> </li> </ul> 

对我而言,哪一个比为不同的结构化树创build多个指令更清洁呢?实质上调用上面的一个树有点错误,它从@ ganaraj的“recursion模板”中获得更多的结果,但允许我们定义我们需要树的模板。

(你可以用一个基于脚本标签的模板来做到这一点,但是它仍然需要坐在实际的树节点之外,而且它还是会觉得有点呃…)

离开这里只是另一个select…

你可以尝试与Angular- Ui-Tree的Angular-Tree-DnD示例,但我编辑过,与表,网格,列表的兼容性。

  • 能够拖放
  • 列表的扩展函数指令(next,prev,getChildren,…)
  • 过滤数据。
  • OrderBy(ver)

根据@ganaraj的回答和@ dnc253的回答 ,我只是对树结构做了一个简单的“指令”,select,添加,删除和编辑特征。

Jsfiddle: http : //jsfiddle.net/yoshiokatsuneo/9dzsms7y/

HTML:

 <script type="text/ng-template" id="tree_item_renderer.html"> <div class="node" ng-class="{selected: data.selected}" ng-click="select(data)"> <span ng-click="data.hide=!data.hide" style="display:inline-block; width:10px;"> <span ng-show="data.hide && data.nodes.length > 0" class="fa fa-caret-right">+</span> <span ng-show="!data.hide && data.nodes.length > 0" class="fa fa-caret-down">-</span> </span> <span ng-show="!data.editting" ng-dblclick="edit($event)" >{{data.name}}</span> <span ng-show="data.editting"><input ng-model="data.name" ng-blur="unedit()" ng-focus="f()"></input></span> <button ng-click="add(data)">Add node</button> <button ng-click="delete(data)" ng-show="data.parent">Delete node</button> </div> <ul ng-show="!data.hide" style="list-style-type: none; padding-left: 15px"> <li ng-repeat="data in data.nodes"> <recursive><sub-tree data="data"></sub-tree></recursive> </li> </ul> </script> <ul ng-app="Application" style="list-style-type: none; padding-left: 0"> <tree data='{name: "Node", nodes: [],show:true}'></tree> </ul> 

JavaScript的:

 angular.module("myApp",[]); /* https://stackoverflow.com/a/14657310/1309218 */ angular.module("myApp"). directive("recursive", function($compile) { return { restrict: "EACM", require: '^tree', priority: 100000, compile: function(tElement, tAttr) { var contents = tElement.contents().remove(); var compiledContents; return function(scope, iElement, iAttr) { if(!compiledContents) { compiledContents = $compile(contents); } compiledContents(scope, function(clone) { iElement.append(clone); }); }; } }; }); angular.module("myApp"). directive("subTree", function($timeout) { return { restrict: 'EA', require: '^tree', templateUrl: 'tree_item_renderer.html', scope: { data: '=', }, link: function(scope, element, attrs, treeCtrl) { scope.select = function(){ treeCtrl.select(scope.data); }; scope.delete = function() { scope.data.parent.nodes.splice(scope.data.parent.nodes.indexOf(scope.data), 1); }; scope.add = function() { var post = scope.data.nodes.length + 1; var newName = scope.data.name + '-' + post; scope.data.nodes.push({name: newName,nodes: [],show:true, parent: scope.data}); }; scope.edit = function(event){ scope.data.editting = true; $timeout(function(){event.target.parentNode.querySelector('input').focus();}); }; scope.unedit = function(){ scope.data.editting = false; }; } }; }); angular.module("myApp"). directive("tree", function(){ return { restrict: 'EA', template: '<sub-tree data="data" root="data"></sub-tree>', controller: function($scope){ this.select = function(data){ if($scope.selected){ $scope.selected.selected = false; } data.selected = true; $scope.selected = data; }; }, scope: { data: '=', } } }); 

是的,这绝对有可能。 这里的问题可能假定为Angular 1.x,但为了将来的参考,我将包含一个Angular 2的例子:

从概念上讲,你所要做的就是创build一个recursion模板:

 <ul> <li *for="#dir of directories"> <span><input type="checkbox" [checked]="dir.checked" (click)="dir.check()" /></span> <span (click)="dir.toggle()">{{ dir.name }}</span> <div *if="dir.expanded"> <ul *for="#file of dir.files"> {{file}} </ul> <tree-view [directories]="dir.directories"></tree-view> </div> </li> </ul> 

然后,您将树对象绑定到模板,并让Angular发挥其魔力。 这个概念显然也适用于Angular 1.x。

这是一个完整的例子: http : //www.syntaxsuccess.com/viewarticle/recursive-treeview-in-angular-2.0

你可以使用angularrecursion注入器: https : //github.com/knyga/angular-recursion-injector

允许你做无限的深度嵌套与条件。 仅在需要时重新编译,只编译正确的元素。 代码没有魔力。

 <div class="node"> <span>{{name}}</span> <node--recursion recursion-if="subNode" ng-model="subNode"></node--recursion> </div> 

其中一个让它工作得更快,更简单的方法就是“–recursion”后缀。

当树结构很大时,Angular(高达1.4.x)在呈现recursion模板时变得非常慢。 尝试了一些这些build议后,我最终创build了一个简单的HTMLstring,并使用ng-bind-html来显示它。 当然,这不是使用Angularfunction的方式

这里展示了一个简单的recursion函数(使用最小的HTML):

 function menu_tree(menu, prefix) { var html = '<div>' + prefix + menu.menu_name + ' - ' + menu.menu_desc + '</div>\n'; if (!menu.items) return html; prefix += menu.menu_name + '/'; for (var i=0; i<menu.items.length; ++i) { var item = menu.items[i]; html += menu_tree(item, prefix); } return html; } // Generate the tree view and tell Angular to trust this HTML $scope.html_menu = $sce.trustAsHtml(menu_tree(menu, '')); 

在模板中,它只需要这一行:

 <div ng-bind-html="html_menu"></div> 

这绕过了所有的Angular的数据绑定,并简单地在recursion模板方法的一小部分时间内显示HTML。

像这样的菜单结构(Linux文件系统的部分文件树):

 menu = {menu_name: '', menu_desc: 'root', items: [ {menu_name: 'bin', menu_desc: 'Essential command binaries', items: [ {menu_name: 'arch', menu_desc: 'print machine architecture'}, {menu_name: 'bash', menu_desc: 'GNU Bourne-Again SHell'}, {menu_name: 'cat', menu_desc: 'concatenate and print files'}, {menu_name: 'date', menu_desc: 'display or set date and time'}, {menu_name: '...', menu_desc: 'other files'} ]}, {menu_name: 'boot', menu_desc: 'Static files of the boot loader'}, {menu_name: 'dev', menu_desc: 'Device files'}, {menu_name: 'etc', menu_desc: 'Host-specific system configuration'}, {menu_name: 'lib', menu_desc: 'Essential shared libraries and kernel modules'}, {menu_name: 'media', menu_desc: 'Mount point for removable media'}, {menu_name: 'mnt', menu_desc: 'Mount point for mounting a filesystem temporarily'}, {menu_name: 'opt', menu_desc: 'Add-on application software packages'}, {menu_name: 'sbin', menu_desc: 'Essential system binaries'}, {menu_name: 'srv', menu_desc: 'Data for services provided by this system'}, {menu_name: 'tmp', menu_desc: 'Temporary files'}, {menu_name: 'usr', menu_desc: 'Secondary hierarchy', items: [ {menu_name: 'bin', menu_desc: 'user utilities and applications'}, {menu_name: 'include', menu_desc: ''}, {menu_name: 'local', menu_desc: '', items: [ {menu_name: 'bin', menu_desc: 'local user binaries'}, {menu_name: 'games', menu_desc: 'local user games'} ]}, {menu_name: 'sbin', menu_desc: ''}, {menu_name: 'share', menu_desc: ''}, {menu_name: '...', menu_desc: 'other files'} ]}, {menu_name: 'var', menu_desc: 'Variable data'} ] } 

输出变成:

 - root /bin - Essential command binaries /bin/arch - print machine architecture /bin/bash - GNU Bourne-Again SHell /bin/cat - concatenate and print files /bin/date - display or set date and time /bin/... - other files /boot - Static files of the boot loader /dev - Device files /etc - Host-specific system configuration /lib - Essential shared libraries and kernel modules /media - Mount point for removable media /mnt - Mount point for mounting a filesystem temporarily /opt - Add-on application software packages /sbin - Essential system binaries /srv - Data for services provided by this system /tmp - Temporary files /usr - Secondary hierarchy /usr/bin - user utilities and applications /usr/include - /usr/local - /usr/local/bin - local user binaries /usr/local/games - local user games /usr/sbin - /usr/share - /usr/... - other files /var - Variable data 

不复杂。

 <div ng-app="Application" ng-controller="TreeController"> <table> <thead> <tr> <th>col 1</th> <th>col 2</th> <th>col 3</th> </tr> </thead> <tbody ng-repeat="item in tree"> <tr> <td>{{item.id}}</td> <td>{{item.fname}}</td> <td>{{item.lname}}</td> </tr> <tr ng-repeat="children in item.child"> <td style="padding-left:15px;">{{children.id}}</td> <td>{{children.fname}}</td> </tr> </tbody> </table> </div> 

控制器代码:

 angular.module("myApp", []). controller("TreeController", ['$scope', function ($scope) { $scope.tree = [{ id: 1, fname: "tree", child: [{ id: 1, fname: "example" }], lname: "grid" }]; }]);