AngularJS动态菜单操作指令


Posted in Javascript onApril 25, 2017

前言    

    在我们创建一个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动态菜单操作指令,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
用于自动添加Digg This!按钮的JavaScript
Dec 23 Javascript
JavaScript 类似flash效果的立体图片浏览器
Feb 08 Javascript
jQuery简单图表peity.js使用示例
May 02 Javascript
JavaScript实现获得所有兄弟节点的方法
Jul 23 Javascript
一道常被人轻视的web前端常见面试题(JS)
Feb 15 Javascript
解析jQueryEasyUI的使用
Nov 22 Javascript
jQuery实现ToolTip元素定位显示功能示例
Nov 23 Javascript
JS检测数组类型的方法小结
Mar 14 Javascript
利用JS实现scroll自定义滚动效果详解
Oct 17 Javascript
微信小程序开发问题之wx.previewImage
Dec 25 Javascript
用Cordova打包Vue项目的方法步骤
Feb 02 Javascript
vue-cli和v-charts实现可视化图表过程解析
Oct 08 Javascript
Angular.js 4.x中表单Template-Driven Forms详解
Apr 25 #Javascript
详解JS中的attribute属性
Apr 25 #Javascript
node.js中debug模块的简单介绍与使用
Apr 25 #Javascript
Node.js利用debug模块打印出调试日志的方法
Apr 25 #Javascript
JS实现禁止高频率连续点击的方法【基于ES6语法】
Apr 25 #Javascript
json的结构与遍历方法实例分析
Apr 25 #Javascript
详谈jQuery中使用attr(), prop(), val()获取value的异同
Apr 25 #jQuery
You might like
Yii框架中 find findAll 查找出制定的字段的方法对比
2014/09/10 PHP
php中异常处理方法小结
2015/01/09 PHP
php Session无效分析资料整理
2016/11/29 PHP
Laravel 实现在Blade模版中使用全局变量代替路径的例子
2019/10/22 PHP
PHP高并发和大流量解决方案整理
2019/12/24 PHP
Alliance vs Liquid BO3 第二场2.13
2021/03/10 DOTA
脚本吧 - 幻宇工作室用到js,超强推荐expand.js
2006/12/23 Javascript
Javascript的常规数组和关联数组对比小结
2012/05/24 Javascript
判断文件是否正在被使用的JS代码
2013/12/21 Javascript
JavaScript实现简单的tab选项卡切换
2016/01/05 Javascript
基于javascript实现tab切换特效
2016/03/29 Javascript
BootStrap中
2016/12/10 Javascript
使用AngularJS 跨站请求如何解决jsonp请求问题
2017/01/16 Javascript
JS Select下拉框(支持输入模糊查询)
2017/02/04 Javascript
Vue中如何实现轮播图的示例代码
2017/07/27 Javascript
bootstrap 路径导航 分页 进度条的实例代码
2018/08/06 Javascript
详解ES6系列之私有变量的实现
2018/11/21 Javascript
解决Ubuntu pip 安装 mysql-python包出错的问题
2018/06/11 Python
Python3.7黑帽编程之病毒篇(基础篇)
2020/02/04 Python
Python做图像处理及视频音频文件分离和合成功能
2020/11/24 Python
python自动生成证件号的方法示例
2021/01/14 Python
HearthSong官网:儿童户外玩具、儿童益智玩具
2017/10/16 全球购物
盖尔斯工厂店:GUESS Factory
2020/01/21 全球购物
请编写一个 C 函数,该函数在给定的内存区域搜索给定的字符,并返回该字符所在位置索引值
2014/09/15 面试题
校园自助餐厅的创业计划书
2013/12/26 职场文书
党员入党表决心的话
2014/03/11 职场文书
年会搞笑主持词
2014/03/27 职场文书
新春寄语大全
2014/04/09 职场文书
副总经理任命书
2014/06/05 职场文书
党员自我评议个人对照检查材料
2014/09/16 职场文书
文明班级申报材料
2014/12/24 职场文书
关爱空巢老人感想
2015/08/11 职场文书
幼儿体育课教学反思
2016/02/16 职场文书
超级实用!五步法则,教你写好年终工作总结
2019/12/05 职场文书
Nest.js参数校验和自定义返回数据格式详解
2021/03/29 Javascript
Java实现二维数组和稀疏数组之间的转换
2021/06/27 Java/Android