2019独角兽企业重金招聘Python工程师标准>>>

前言

在我们创建一个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结构。

参考资料

【美】Ari Lerner 著《AngularJS 权威教程》

转载时请注明:来自w-rain的个人博客

转载于:https://my.oschina.net/myrainspace/blog/886078

AngularJS之动态菜单操作指令相关推荐

  1. Skill加载和管理(设置相对路径与动态菜单)

    本文作者原创,因资料来源于EDA365,先发于EDA365,在本网站作一备份. 论坛中大神很多,但小白也不想花时间学怎么开发skill,只要可以加载使用就好了.群中的内容操作多数都可直接拿来使,可当下 ...

  2. 十个效果酷炫的Linux系统操作指令(像黑客帝国般的效果~)

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 想想电影黑客帝国中的画面,估计会令很多人都叹为观止,其实最简单的只 ...

  3. 孙鑫老师教学视频学习笔记——单文档中菜单操作

    静态实现菜单操作 创建和删除一个标记菜单: 在框架类CMainFrame中的OnCreate消息响应函数中添加:     GetMenu()->GetSubMenu(0)->CheckMe ...

  4. vue 获取动态域名_vue项目接口域名动态获取操作

    需求: 接口域名是从外部 .json 文件里获取的. 思路: 在开始加载项目前 进行接口域名获取,然后重置 接口域名的配置项. 实现: 1.config/index.js 文件 进行基础配置 impo ...

  5. Django Vue实现动态菜单、动态权限

    随着前后端分离架构的流行,在 web 应用中,RESTful API 几乎已经成为了开发者主要选择,它使得客户端和服务端不需要保存对方的详细信息,也就是无状态性,但是这样在项目中需要动态菜单和动态权限 ...

  6. Vue3 中如何加载动态菜单?

    松哥之前写了两篇文章和大家分享了 TienChin 项目中的菜单数据问题,还没看过的小伙伴请戳这里: Vue 里,多级菜单要如何设计才显得专业? TienChin 项目动态菜单接口分析 这两篇文章主要 ...

  7. Altium Designer 2020 学习笔记(一)-----原理图及原理图库部分(配动态图操作演示)

    本篇文章主要与大家简要分享一下,我在AD学习过程中的一些学习笔记,本篇文章主要关于原理图及原理图库部分. 本系列文章链接: -------------------------------------- ...

  8. 微信公众号开发(一) -- 自定义菜单 动态菜单

    简单修改一下appid.appsecret 代码可直接复制使用 自定义菜单 //获取token值 $appid = ''; //微信支付申请对应的公众号的APPID $appsecret = ''; ...

  9. “动吧“ - crud 练习 part7 - Shiro安全框架简介 - 58~60 - 、[扩展] - 动态菜单 - 60

    名词: ------------------------------------------- subject - 主体.起因 - (登录到==>登录成功后 , 全部的记录) principal ...

最新文章

  1. React入门---事件与数据的双向绑定-9
  2. 申请Windows2008R2学生key
  3. vue指令:v-once 元素和组件只渲染一次,不会随着数据的改变而改变
  4. 为什么要选择学Web前端?无法反驳的4大理由
  5. 什么时候找到HAVE_CONFIG_H的定义啊
  6. 5 高可靠,构建RabbitMQ集群架构
  7. bzoj3545/bzoj3551 [ONTAK2010]Peaks/Peaks加强版
  8. 今天我理解了妈妈为什么会被电信诈骗
  9. python tkinter 定时_如何使用tkinter创建计时器?
  10. 王校长一分钟能吃多少热狗?| 小游戏 1
  11. 【Visio开发工具】用Visio绘制封闭曲面并填充颜色
  12. LDR6028充电OTG直播线直播声卡音频转接器最具性价比方案
  13. excel如何深度隐藏sheet呢?
  14. SRS之SrsConfig类
  15. 前端培训丁鹿学堂:css伪类选择器(一)
  16. [停更] 微信平台上的DRL [停更]
  17. oracle索引整理
  18. html单文档编辑器,用VC++MFC做文本编辑器(单文档模式)
  19. 华为计算机魔术,科技魔术秀 如何让手机秒变电脑
  20. 飞秋 飞秋2010 飞秋2010下载 飞秋下载2010正式版

热门文章

  1. 点到线段的距离计算方法
  2. CentOS6.4之文本编辑器Vi/Vim
  3. Thinking in java 多线程导致数组越界崩溃的BUG
  4. [攻克存储] 掌握SDRAM/DDR的结构与寻址
  5. 一个C#睡觉前的夜晚
  6. B-Trees【设计数据密集型应用】
  7. ArcGIS Engine中的Symbols详解
  8. 为什么人人都该懂点LLVM
  9. C# params关键字
  10. Zabbix 3.2 短信报警配置