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 相关文章推荐
用JavaScrpt实现文件夹简单轻松加密的实现方法图文
Sep 08 Javascript
javascript web对话框与弹出窗口
Feb 22 Javascript
react高阶组件经典应用之权限控制详解
Sep 07 Javascript
vue 运用mock数据的示例代码
Nov 07 Javascript
原生JavaScript实现Ajax异步请求
Nov 19 Javascript
关于js的三种使用方式(行内js、内部js、外部js)的程序代码
May 05 Javascript
JavaScript中import用法总结
Jan 20 Javascript
Vue 组件参数校验与非props特性的方法
Feb 12 Javascript
使用JavaScript解析URL的方法示例
Mar 01 Javascript
Vue-CLI 项目在pycharm中配置方法
Aug 30 Javascript
vue使用swiper.js重叠轮播组建样式
Nov 14 Javascript
JavaScript实现浏览器网页自动滚动并点击的示例代码
Dec 05 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
真正面向对象编程:PHP5.01发布
2006/10/09 PHP
php下过滤HTML代码的函数
2007/12/10 PHP
PHP 替换模板变量实现步骤
2009/08/24 PHP
PHP实现HTML生成PDF文件的方法
2014/11/07 PHP
PHP将字符分解为多个字符串的方法
2014/11/22 PHP
Thinkphp开发--集成极光推送
2017/09/15 PHP
js实现上传图片之上传前预览图片
2013/03/25 Javascript
异步JavaScript编程中的Promise使用方法
2015/07/28 Javascript
使用JavaScript脚本无法直接改变Asp.net中Checkbox控件的Enable属性的解决方法
2015/09/16 Javascript
Node.js实现发送邮件功能
2017/11/06 Javascript
解决Webpack 热部署检测不到文件变化的问题
2018/02/22 Javascript
浅谈angularJS2中的界面跳转方法
2018/08/31 Javascript
详解使用React.memo()来优化函数组件的性能
2019/03/19 Javascript
react写一个select组件的实现代码
2019/04/03 Javascript
微信小程序获取用户绑定手机号方法示例
2019/07/21 Javascript
vue 集成 vis-network 实现网络拓扑图的方法
2019/08/07 Javascript
基于js实现的图片拖拽排序源码实例
2020/11/04 Javascript
[50:58]2018DOTA2亚洲邀请赛 4.1 小组赛 B组 Mineski vs EG
2018/04/03 DOTA
Python3连接MySQL(pymysql)模拟转账实现代码
2016/05/24 Python
Python实现压缩与解压gzip大文件的方法
2016/09/18 Python
Sanic框架Cookies操作示例
2018/07/17 Python
wxpython自定义下拉列表框过程图解
2020/02/14 Python
Python中如何引入第三方模块
2020/05/27 Python
Python3.8安装Pygame教程步骤详解
2020/08/14 Python
python 实现数据库中数据添加、查询与更新的示例代码
2020/12/07 Python
Python Selenium库的基本使用教程
2021/01/04 Python
Html5 实现微信分享及自定义内容的流程
2019/08/20 HTML / CSS
HTML5 script元素async、defer异步加载使用介绍
2013/08/23 HTML / CSS
德国最大的拼图在线商店:Puzzle.de
2016/12/17 全球购物
写给爸爸的道歉信
2014/01/15 职场文书
兰兰过桥教学反思
2014/02/08 职场文书
工程造价专业求职信
2014/07/17 职场文书
自查自纠工作总结
2014/10/15 职场文书
2014年小学班主任工作总结
2014/11/08 职场文书
研讨会致辞
2015/07/31 职场文书
Golang ort 中的sortInts 方法
2022/04/24 Golang