AngularJS动态菜单操作指令

前言 

在我们创建一个angularJS应用的时候,菜单往往往是不可或缺的元素之一。也许在我们静态菜单的时候不会发现在指令中操作菜单收缩、折叠展开没有任何问题,因为我们在操作之前,页面元素渲染已经完成,所以在指令里面通过element查找目标元素可以成功。但是一旦我们的菜单的数据不是静态而是通过后台接口加载动态数据渲染,我们会发现本来在静态写好的指令操作,在转变为动态数据加载之后,怎么也没法查找到想要的目标元素。

遇到如此问题,开始觉得好奇葩的,当然这也是吐槽一下,还是得好好解决问题的,痛定失痛,决心好好理清思路,分析一下问题原因。首先我们先了解一下AngularJS的生命周期。

AngularJS的生命周期

在AngularJS应用启动前,它们会以HTML文本的形式保存在文本编辑器中。应用启动后会进行编译和链接,作用域会同HTML进行绑定,应用可以对用户在HTML中进行的操作进行实时响应。AngularJS的生命周期主要有两个主要阶段:一个是编译阶段,一个是链接阶段。

AngularJS生命周期-编译阶段

在编译阶段,AngularJS会遍历整个HTML文档并根据JavaScript中的指令定义来处理页面上声明的指令。每一个指令模板中可能有另一个指令,另一个指令也有可能会有自己的模板。AngularJS调用HTML文档根部的指令时,会遍历其中所有的模板,模板中可能含有模板的指令。如果一个元素已经有一个含有模板的指令,永远不要对其用另一个指令进行修饰,只有最高优先级的指令中的模板会被编译。

一旦对指令和其中的子模板进行遍历或编译,编译后的模板会返回一个叫做模板函数的函数。在这个时候的DOM树还没有进行数据绑定,此时对DOM树操作只会有很少的性能开销,ng-repeat和ng-transclude等内置指令会在这个时候对还未进行数据绑定的DOM进行操作。比如ng-repeat,它会遍历指定的数组或对象,在数据绑定之前构建对应的DOM结构,然后将新的DOM(编译后的DOM)传递给指令生命周期中的下一阶段,链接阶段。一个指令的DOM一旦编译完成,就可以立即通过编译函数对其进行访问,编译函数的签名包含有访问指令声明所在的元素(tElements)及该元素对其他属性(tAttrs)的方法。

compile返回对象或函数,compile()函数负责对模板DOM进行转换,link()函数负责将作用域和DOM进行转换。

//...
compile: function(tEle,tAttrs,transcludeFn){
 var tplEl = angular.element('<div>' +'<h2></h2>'+'</div>');
 var h2 = tplEl.find('h2');
 h2.attr('type',tAttrs.type);
 h2.attr('ng-model',tAttrs.ngModel);
 h2.val('hello');
 tEle.replaceWith(tplEl);
 return function(scope, ele, attrs){
  //连接函数
 };
}
//...

 AngularJS生命周期-链接阶段

link函数创建可以操作DOM的指令,链接函数是可选的。定义了编译函数,返回链接函数,当两个函数都定义了,编译函数会重载链接函数。

//下面2种定义指令的放松在功能上是完全一样的
angular.module('myApp',[])
.directive('myDirective', function (){
 return {
 pre: function (tElement, tAttrs, transclude){
 //在子元素被链接之前执行,之后调用‘link'函数无法定位链接的元素

 },
 post: function (scope, iElement, iAttrs, controllers){
   //在子元素被链接之后执行
 }
 }
});
angular.module('myApp',[])
.directive('myDirective', function (){
 return {
 link: function (scope, ele, attrs){
  return {
  pre: function (tElement, tAttrs, transclude){
  //在子元素被链接之前执行,之后调用‘link'函数无法定位链接的元素
  },
  post: function (scope, iElement, iAttrs, controllers){
    //在子元素被链接之后执行
  }
  }
 }
 }
});

当定义了编译函数来取代链接函数时,链接函数使我们能提供给返回对象的第二个方法,也就是postLink函数。链接函数会在模板编译并同作用域进行链接后被调用,它负责设置事件监听器,监听数据变化和实时的操作DOM。

//链接函数签名
link: function(scope, element, attrs){
 //操作DOM
}
//含require选项, require someContainer
link: function(scope, element, attrs, someContainer){
 //在这里操作DOM,可以访问require指定的控制器
}
  • scope--指令用来在其内部注册监听器的作用域;
  • element--参数代表实例元素,指使用此指令的元素;
  • atrrs--代表实例属性,是一个由定义在元素上的属性组成的标准化列表,可以在所有指令的链接函数间共享,会以javascript对象的形式进行传递;
  • controller 参数指向require选项定义的控制器。没有设置require选项,controller的参数为undefined;

控制器在所有的指令间共享,因此指令可以将控制器当作通信通道(公共API),如果设置多个require,这个参数是一个控制器实例组成的数组,而不是一个单独的控制器。

问题剖析

在通过对AngularJS生命周期的理解,我们可以清晰地认识到动态菜单为什么绑定在链接阶段上的DOM操作没有成功,由于ng-repeat的原因,我对DOM树操作没找到DOM元素。因为在封装成一个菜单指令组件的时候,我内部的菜单数据加载使用ng-repeat实现,所以只有在这个时候才能在ng-repeat内部绑定对DOM树的操作。

最初的写法:

//html
<menu-bar>
`````
<div ng-repeat="ml in menuLists">
 ``````
 <div ng-repeat="mls in ml.secondLists">
 ``````
 <div ng-repeat="mlt in mls.thirdLists">
 ``````
 </div>
 ``````
 </div>
 ``````
</div>
``````
</menu-bar>
//directive
angular.module('',[]).directive('menuBar',function (){
 return {
 restrict: 'E',
 replace: true,
 link: function (scope, element, attr){
 //操作菜单的逻辑代码
 }
 }
});

这种写法,在link里面操作菜单逻辑的代码没有被触发,尼玛,angularjs的检测机制也没用,因为ng-repeat的原因导致DOM操作事件没有被挂载到DOM上,所以想操作菜单不可能成功。但是,如果ng-repeat的内容是静态存在的,link函数里面的操作是可以实现的。

修改后的写法:

//html
<div ng-repeat="ml in menuLists">
 ``````
 <div ng-repeat="mls in ml.secondLists">
 ``````
 <menu-bar>
 ``````
 <div ng-repeat="mlt in mls.thirdLists">
 ``````
 <menu-bar>
  ``````
 </menu-bar>
 ``````
 </div>
 ``````
 </menu-bar>
 ``````
 </div>
 ``````
</div>
//directive
angular.module('',[]).directive('menuBar',function (){
 return {
 restrict: 'E',
 replace: true,
 link: function (scope, element, attr){
 //操作菜单的逻辑代码
 }
 }
});

修改之后我们将我们操作动态加载的DOM结构的指令放入ng-repeat中,此时逻辑正常执行,在link函数中能打印出DOM结构。

以上所述是小编给大家介绍的AngularJS动态菜单操作指令,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Bootstrap3 datetimepicker控件使用实例

    Bootstrap3 日期+时间选择控件的使用方法,供大家参考,具体内容如下 1.支持日期选择,格式设定 2.支持时间选择 3.支持时间段选择控制 4.支持中文 官网地址:http://eonasdan.github.io/bootstrap-datetimepicker/ git地址:https://github.com/Eonasdan/bootstrap-datetimepicker moment语言包:https://github.com/moment/moment datetimepi

  • angularjs+bootstrap菜单的使用示例代码

    需求背景: 使用yo angular生成的项目默认主页是这样的: body部分涉及具体的业务内容,后续研究.这里主要研究菜单项.页眉的处理. 页脚处理: 自动生成的项目中,菜单和页脚设置都是在index.html文件中实现的. # index.html <div class="footer"> <div class="container"> <p><span class="glyphicon glyphicon-h

  • Bootstrap3制作搜索框样式的方法

    Bootstrap3中的很多样式,都是需要我们进行摸索着学习的,在制作类似百度的素搜索框时,一开始会不知道怎么做,其实很简单,利用内联图标方式就可以实现 <div class="input-group"> <input type="text" class="form-control input-lg"><span class="input-group-addon btn btn-primary"&

  • AngularJS实现树形结构(ztree)菜单示例代码

    树形结构 树形结构有多种形式和实现方式,zTree可以说得上是一种比较简洁又美观的且实现起来也相对简单.zTree是一个依靠jQuery实现的多功能"树插件".它最大的优点是配置灵活,只要id与pid的值相同就可形成一个简单的父子结构.再加上免费开源,使用zTree的人越来越多. 效果图如下 首先你需要知道AngularJS的双向数据绑定是什么才可以更好的理解下面的代码,想了很久才想出用下面的代码来实现左侧菜单树形结构 要实现上面的功能你需要操作如下步骤: 在HTML标签内添加ng-a

  • 实例详解AngularJS实现无限级联动菜单

    多级联动菜单是常见的前端组件,比如省份-城市联动.高校-学院-专业联动等等.场景虽然常见,但仔细分析起来要实现一个通用的无限分级联动菜单却不一定像想象的那么简单.比如,我们需要考虑子菜单的加载是同步的还是异步的?对于初始值的回填发生在前端还是后端?如果异步加载,是否对于后端API的返回格式有严格的定义?是否容易实现同步.异步共存?是否可以灵活的支持各类依赖关系?菜单中是否有空值选项?--一系列的问题都需要精心处理. 带着这些需求搜索了一圈,不太出乎意料,并没有能在AngularJS的生态中找到一

  • Bootstrap3 多选和单选框(checkbox)

    多选框(checkbox)用于选择列表中的一个或多个选项,而单选框(radio)用于从多个选项中只选择一个. 设置了 disabled 属性的单选或多选框都能被赋予合适的样式.对于和多选或单选框联合使用的 <label> 标签,如果也希望将悬停于上方的鼠标设置为"禁止点击"的样式,请将 .disabled 类赋予 .radio..radio-inline..checkbox..checkbox-inline 或 <fieldset>. 默认外观(堆叠在一起) &

  • AngularJS+Bootstrap3多级导航菜单的实现代码

    将介绍如何用AngularJS构建一个强大的web前端系统.angularjs是由Google团队开发的一款非常优秀web前端框架.在当前如此多的web框架下,AngularJS能脱颖而出,从架构设计上就高人一等,双向数据绑定,依赖注入,指令,MVC,模板.Angular.js创新地把后台技术融入前端开发,扫去jQuery一度的光芒.用angularjs就像写后台代码,更规范,更结构化,更可控. 本文将介绍如何实现多限级导航菜单. 目录 1.静态多级菜单实现 2.动态多级菜单实现 1. 静态多级

  • AngularJS折叠菜单实现方法示例

    本文实例讲述了AngularJS折叠菜单实现方法.分享给大家供大家参考,具体如下: <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="bootstrap.min.css&quo

  • 购物车前端开发(jQuery和bootstrap3)

    作为一名不在软件公司工作的软件工程师,不仅要会写后台代码(PHP/JAVA/SQL...),还是兼顾前端工程师的工作(html/javascript/css...).下面就来分享一个在实际工作项目中使用到的购物车的前端开发. 这里分享的仅仅是针对购物车的操作(产品数量的增加减少,删除购物车中产品项),假设购物车中已经放有若干产品.闲话少说,先上两张效果图. HTML代码如下:这里使用到了JQuery1.11和bootstrap3 . <!DOCTYPE html> <html> &

  • AngularJs中Bootstrap3 datetimepicker使用实例

    关于datetimepicker的使用,参考:http://www.jb51.net/article/99896.htm 在AngularJs中使用实例: HTML代码: <div class="container" ng-app="myApp" ng-controller="myCtrl"> <div class="row"> <div class='col-sm-6'> <div

随机推荐