Angular中实现树形结构视图

前端之家收集整理的这篇文章主要介绍了Angular中实现树形结构视图前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

近两年当中使用Angular开发过很多项目,其中也涉及到一些树形结构视图的显示,最近的在项目中封装了大量的组件,一些组件也有树形结构的展示,所以写出来总结一下。

相信大家都知道,树结构最典型的例子就是目录结构了吧,一个目录可以包含很多子目录,子目录又可以包含若干个子孙目录,那咱们今天就以目录结构为例来说明一下Angular中树结构的实现。

首先,我们希望封装一个组件,用于显示整个目录的树形机构,代码如下:

<!DOCTYPE html>
<html ng-app="treeDemo">
  <body>
    <div ng-controller="TreeController">
        <folder-tree current-folder="folder"></folder-tree>
    </div>
    <script src="node_modules/angular/angular.min.js"></script>
    <script src="js/app.js"></script>
  </body>
</html>
就像上面的代码一样,我们直接声明folder-tree标签,将controller中的folder数据作为参数传递进去,预期会显示出一个完整的树状目录结构。接下来我们就来定义模块,控制器,以及相应的指令部分:
angular.module('treeDemo',[])
  .controller("TreeController",function($scope) {
    $scope.folder = {
        name: 'techs',children: [
            {
                name: 'server-side',children: [
                    {
                        name: 'Java'
                    },{
                        name: 'Python'
                    },{
                        name: 'Node'
                    }
                ]
            },{
                name: 'front-end',children: [
                    {
                        name: 'jQuery'
                    },{
                        name: 'Angular'
                    },{
                        name: 'React'
                    }
                ]
            }
        ]
    }
  })
  .directive("folderTree",function() {
    return {
        restrict: "E",scope: {
            currentFolder: '='
        },templateUrl: 'template/tree.html'
    };
  });
如上所述,在控制器中我们在$scope中绑定了一个folder的数据对象,包含整个的目录结构数据,接着定义了folderTree指令,它会使用tree.html作为模板进行视图渲染,我们这就来look look模板中的内容
<p>{{currentFolder.name}}</p>
<ul>
	<li ng-repeat="subfolder in currentFolder.children">
		<folder-tree current-folder="subfolder"></folder-tree>
	</li>
</ul>
可以看到,在模板中有个很重要的环节,那就是使用ng-repeat指令遍历当前目录的子集,然后再次使用folder-tree组件来渲染相应的子目录,这种方式简直是完美,现在我们来看看渲染的结果:

这种在模板中嵌套使用指令的方法很完美,只可惜低版本的Angular中是无法实现的,会出现无限递归循环,造成页面假死,这是Angular本身的解析机制造成的。经测试,Angular 1.5.0及以上版本是没有问题的,但在Angular 1.4.9及以下版本中就会运行失败。假如你在项目中真的使用了低版本的Angular并且造成运行失败,我们还可以稍微改动一下模板,采用ng-include来实现同样的功能

<p>{{currentFolder.name}}</p>
<ul>
	<li ng-repeat="subfolder in currentFolder.children" 
		ng-include="'template/tree.html'" 
		ng-init="currentFolder = subfolder">
	</li>
</ul>

我们在模板中去掉了folder-tree指令,使用了ng-include指令,再次将tree.html模板引入进来,需要注意的是,因为ng-include会创建一个独立的作用域,为了让其正确的引用到currentFolder变量,我们需要在每个ng-include中初始化currentFolder,将其赋值为ng-repeat中的当前subfolder,另外,别忘了ng-include指令中模板路径前后加上单引号。

这样确实可以,但是你可能觉得有些遗憾,没能使用最好的解决方案来实现这个树结构。

其实这个问题早就有人吐槽了,令人惊喜的是,有个叫Mark的伙计写了一个service专门解决这个问题:

/* 
 * An Angular service which helps with creating recursive directives.
 * @author Mark Lagendijk
 * @license MIT
 */
angular.module('RecursionHelper',[]).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);
					}
				}
			};
		}
	};
}]);
我们只需引入这个模块,然后在directive中使用RecursionHelper这个service:
angular.module('treeDemo',['RecursionHelper'])
  .controller("TreeController",function(RecursionHelper) {
    return {
        restrict: "E",templateUrl: 'template/tree.html',compile: function(element) {
            return RecursionHelper.compile(element,function(scope,iElement,iAttrs,controller,transcludeFn){
                // Define your normal link function here.
                // Alternative: instead of passing a function,// you can also pass an object with 
                // a 'pre'- and 'post'-link function.
            });
        }
    };
  });
上面代码中,我们在创建treeDemo模块时引入RecursionHelper模块,然后在创建folderTree指令时使用RecursionHelper服务,并且在compile方法调用RecursionHelper的compile方法,即可修复上面的问题。 加入这个服务之后,我们就能够愉快的使用嵌套指令了。

猜你在找的Angularjs相关文章