Angular中实现树形结构视图实例代码


Posted in Javascript onMay 05, 2017

近两年当中使用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本身的解析机制造成的。经测试,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 
      // 如果link参数是对象类型link:{pre: function(...){}, post: function(...){}}则不做处理 
      // 如果link参数是函数类型则将其作为post-link函数在$compile之后调用 
      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,调用其compile手动编译指令对应的元素节点:

angular.module('treeDemo', ['RecursionHelper']) 
 .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(RecursionHelper) { 
  return { 
    restrict: "E", 
    scope: { 
      currentFolder: '=' 
    }, 
    templateUrl: 'template/tree.html', 
    compile: function(element) { 
      // 我们这里使用RecursionHelper的compile方法编译指令当前元素,这里第二个参数指定一个函数,相当于常用的link函数 
      // 当然我们也可以指定一个对象,里面包含pre和post函数,分别对应pre-link和post-link 
      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. 
 
        // 这里可以往scope中绑定一些变量 
        scope.variable = 'hello world'; 
      }); 
    } 
  }; 
 });

在上面代码中,我们在创建treeDemo模块时引入RecursionHelper模块,然后在创建folderTree指令时使用RecursionHelper服务,并且在compile方法中调用RecursionHelper的compile方法,即可修复上面的问题。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
仅img元素创建后不添加到文档中会执行onload事件的解决方法
Jul 31 Javascript
设为首页和收藏的Javascript代码(亲测兼容IE,Firefox,chrome等浏览器)
Nov 18 Javascript
Bootstrap每天必学之进度条
Nov 30 Javascript
JavaScript进阶练习及简单实例分析
Jun 03 Javascript
对Js OOP编程 创建对象的一些全面理解
Jul 26 Javascript
AngularJS框架的ng-app指令与自动加载实现方法分析
Jan 04 Javascript
weex里Vuex state使用storage持久化详解
Sep 09 Javascript
Vue.js实现可配置的登录表单代码详解
Mar 29 Javascript
JS实现的input选择图片本地预览功能示例
Aug 29 Javascript
详解Vue.js使用Swiper.js在iOS
Sep 10 Javascript
vue-drawer-layout实现手势滑出菜单栏
Nov 19 Vue.js
JavaScript前端面试组合函数
Jun 21 Javascript
node.JS md5加密中文与php结果不一致的解决方法
May 05 #Javascript
jquery中封装函数传递当前元素的方法示例
May 05 #jQuery
使用JS在浏览器中判断当前网络连接状态的几种方法
May 05 #Javascript
js实现倒计时关键代码
May 05 #Javascript
javascript 中的继承实例详解
May 05 #Javascript
JavaScript函数表达式详解及实例
May 05 #Javascript
Node.js中的http请求客户端示例(request client)
May 04 #Javascript
You might like
工厂模式在Zend Framework中应用介绍
2012/07/10 PHP
php中文乱码怎么办如何让浏览器自动识别utf-8
2014/01/15 PHP
php编写的一个E-mail验证类
2015/03/25 PHP
全新Mac配置PHP开发环境教程
2016/02/03 PHP
PHP5.3连接Oracle客户端及PDO_OCI模块的安装方法
2016/05/13 PHP
php接口技术实例详解
2016/12/07 PHP
Docker配置PHP开发环境教程
2016/12/21 PHP
php读取本地json文件的实例
2018/03/07 PHP
DHTML Slide Show script图片轮换
2008/03/03 Javascript
JS高级笔记
2011/07/13 Javascript
JavaScript动态创建div属性和样式示例代码
2013/10/09 Javascript
jquery如何实现锚点链接之间的平滑滚动
2013/12/02 Javascript
离开当前页面前使用js判断条件提示是否要离开页面
2014/05/02 Javascript
javascript 小数取整简单实现方式
2014/05/30 Javascript
JS中的THIS和WINDOW.EVENT.SRCELEMENT详解
2015/05/25 Javascript
JS正则表达式之非捕获分组用法实例分析
2016/12/28 Javascript
requirejs按需加载angularjs文件实例
2017/06/08 Javascript
详解用webpack的CommonsChunkPlugin提取公共代码的3种方式
2017/11/09 Javascript
基于node下的http小爬虫的示例代码
2018/01/11 Javascript
iview table render集成switch开关的实例
2018/03/14 Javascript
vue观察模式浅析
2018/09/25 Javascript
javascript实现画板功能
2020/04/12 Javascript
python flask实现分页效果
2017/06/27 Python
Python调用服务接口的实例
2019/01/03 Python
pandas进行时间数据的转换和计算时间差并提取年月日
2019/07/06 Python
Python使用graphviz画流程图过程解析
2020/03/31 Python
使用Pycharm(Python工具)新建项目及创建Python文件的教程
2020/04/26 Python
30行Python代码实现高分辨率图像导航的方法
2020/05/22 Python
基于Keras 循环训练模型跑数据时内存泄漏的解决方式
2020/06/11 Python
Python爬虫之Selenium库的使用方法
2021/01/03 Python
全球精选男装和家居用品:Article
2020/04/13 全球购物
行政管理人员精品工作推荐信
2013/11/04 职场文书
采购部部门职责
2013/12/15 职场文书
美容院员工规章制度
2015/08/05 职场文书
MySQL高速缓存启动方法及参数详解(query_cache_size)
2021/07/01 MySQL
Python实现数据的序列化操作详解
2022/07/07 Python