基于angular中的重要指令详解($eval,$parse和$compile)


Posted in Javascript onOctober 21, 2016

在angular的服务中,有一些服务你不得不去了解,因为他可以说是ng的核心,而今天,我要介绍的就是ng的两个核心服务,$parse和$compile。其实这两个服务讲的人已经很多了,但是100个读者就有100个哈姆雷特,我在这里讲讲自己对于他们两个服务的理解。

大家可能会疑问,$eval呢,其实他并不是一个服务,他是scope里面的一个方法,并不能算服务,而且它也基于parse的,所以只能算是$parse的另一种写法而已,我们看一下ng源码中$eval的定义是怎样的就知道了

$eval: function(expr, locals) {
    return $parse(expr)(this, locals);
   },

相信看完源码大家就明白了吧,好了,现在就开始两种核心服务的讲解了,如果感觉我说的不对的话,欢迎在评论区或者私聊指出,免得祸害其他读者。

再讲这两个服务的时候,我要先讲一个在本贴的概念:上下文

我相信,很多人都听过这个“上下文”,但是可能有点模糊,在我这里给大家解释解释看看大家接不接受这个说法。

还记得angular的数据绑定吗?比如:我现在有个有个叫TestCtrl的控制器,他的内容如下:

.controller('TestCtrl', function($scope) {
    $scope.test = "Boo!!!"
  })

而在html中我们的代码是这样的

<body ng-controller="TestCtrl">
  {{test}}
</body>

那么,大家不用想都知道结果了,页面上肯定会显示 Boo!!!的字样。

但是如果我删掉ng-controller的指令呢?也就是我没有在html申明控制器,你直接绑定{{test}}会如何呢?

结果只有一个,那就是页面啥都没有(ps:因为你申明了ng-app)。讲到这里大家明白了吗?

控制器就相当于一个上下文的容器,真正的上下文其实是$scope,当页面绑定test,如果申明了控制器,当前上下文就是控制器里面的$scope,ng会去找一下你这个控制器的上下文$scope有没有test,如果有,他当然就显示出来了,但是你不申明控制器的时候呢?他的上下文容器就是ng-app了,那么他真正的上下文就是$rootScope,这个时候他就会寻找$rootScope有没有test。

好了,上下文的概念已经讲完了,其实挺容易理解的,基本上和this非常相似

那么言归正传,我们开始讲$parse,首先我们要看的是ng的API文档

var getter = $parse('user.name');
var setter = getter.assign;
var context = {user:{name:'angular'}};
var locals = {user:{name:'local'}};

expect(getter(context)).toEqual('angular');
setter(context, 'newValue');
expect(context.user.name).toEqual('newValue');
expect(getter(context, locals)).toEqual('local');

大家看到的是ng文档里面对于$parse服务性价比最高的几行代码,

getter和setter就是大家所熟知的get方法和set方法了,context和locals仅仅是json对象而已,目的就是模拟上下文关系

大家看到的下面四个语句最终都能通过测试,现在我们一个个来分析,分析之前我要解释一遍什么叫$parse

$parse服务其实就是一种解析表达式的功能,就像ng-model=“test”,你在html中写这个东西谁知道你ng-model=“test”中,其实你想绑定的是当前控制器(上下文容器)中scope(上下文)中的test里面的值,ng就是通过$parse服务去帮助你解析这个表达式的,所以在调用$parse服务的时候你需要传递上下文对象,让ng知道你是要去哪里的scope(上下文)去找你这个test。

所以我们看到第一行测试代码是这样的:

getter(context)).toEqual('angular') //实际上就是 $parse('user.name')(context)

在这个context就是上下文,他能返回“angular“这个字符串的原理就是经过这三步的:

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.在上下问对象中寻找表达式,最终获得“angular“这个字符创

所以这句测试代码是成功的。

我们看第二个方法setter方法

setter(context, 'newValue');//实际上就是 $parse('user.name').assign(context, 'newValue')
expect(context.user.name).toEqual('newValue');//测试数据上下文的值是否被改变

这里的setter方法其实是改变值得方法

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.改变表达式中的值,将上下文对象编程{user:{name:'newValue'}}

于是上下文对象发生了改变,重新用getter方法去获取表达式的时候,上下文已经从{user:{name:'angular'}} --> {user:{name:'newValue'}},最后获取的表达式的值自然就是“newValue”了,所以测试代码也是通过的。

expect(getter(context, locals)).toEqual('local');//实际上就是$parse('user.name')(context, locals)

这里要表现的其实是上下文的替换功能。

在getter的方法中我们不仅可以选择第一个上下文,但是如果我们传递了第二个参数,那么第一个上下文就会被第二个上下文覆盖,注意是覆盖.

1.获取当前的表达式user.name

2.获取当前的上下文对象{user:{name:'angular'}}

3.覆盖当前的上下文{user:{name:'local'}}

4.获取解析之后表达式的值

重新回到$eval这个地方,我们看待$eval源码中可以看出$eval只有get功能,而没有set功能,但是有些时候我们可以选择传递第二个上下文,来达到修改值得效果。

在这里$parse服务就已将说完了,接下来就是$compile

--------------------------------------------------

如果你了解了$parse的概念之后,我想$compile也差不多理解了,其实和$parse很像。但是他是解析一段html代码的,他的功能就是将死模板变成活模板,也是指令的核心服务。

比如你有一段html代码 <h1>{{test}}</h1>,如果你将这段代码直接放在html代码里面,它所呈现的内容是怎样的我不说大家也应该懂。这就是死模板了,而所谓的活模板,就是这里面的数据全部经过了数据的绑定 {{test}}会自动找到当前的上下文,来绑定数据。最后显示出来的 就是活模板,也就是经过数据绑定的模板。

$compile('死模板')(上下文对象),这样就将死模板编程了活模板,你就可以对这段活的html代码做操作了,例如增加到当前节点,等等。

但是在指令中,她会返回两个函数pre-link和post-link

第一个执行的是pre-link,它对于同一个指令的遍历顺序是从父节点到子节点的遍历,在这个阶段,dom节点还没有稳定下来,无法做一些绑定事件的操作,但是我们可以在这里进行一些初始化数据的处理。

第二个执行的是post-link,也就是我们常说的link函数,他是从子节点到父节点遍历的,在这个阶段,DOM节点已经稳定下来了,我们一般会在这里进行很多的操作。

以上就是小编为大家带来的基于angular中的重要指令详解($eval,$parse和$compile)全部内容了,希望大家多多支持三水点靠木~

Javascript 相关文章推荐
Jquery 获取表单text,areatext,radio,checkbox,select值的代码
Nov 12 Javascript
jQuery向上遍历DOM树之parents(),parent(),closest()之间的区别
Dec 02 Javascript
Js保留小数点的4种效果实现代码分享
Apr 12 Javascript
简易的投票系统以及js刷票思路和方法
Apr 07 Javascript
体验jQuery和AngularJS的不同点及AngularJS的迷人之处
Feb 02 Javascript
JS三级可折叠菜单实现方法
Feb 29 Javascript
微信小程序 教程之wxapp视图容器 scroll-view
Oct 19 Javascript
jQuery EasyUI 为Combo,Combobox添加清除值功能的实例
Apr 13 jQuery
解决node修改后需频繁手动重启的问题
May 13 Javascript
JavaScript继承定义与用法实践分析
May 28 Javascript
Vue scrollBehavior 滚动行为实现后退页面显示在上次浏览的位置
May 27 Javascript
vue设置全局访问接口API地址操作
Aug 14 Javascript
BootStrap轻松实现微信页面开发代码分享
Oct 21 #Javascript
浅谈angularjs module返回对象的坑(推荐)
Oct 21 #Javascript
浅谈Angularjs link和compile的使用区别
Oct 21 #Javascript
JavaScript高仿支付宝倒计时页面及代码实现
Oct 21 #Javascript
基于angularJS的表单验证指令介绍
Oct 21 #Javascript
全屏滚动插件fullPage.js使用实例解析
Oct 21 #Javascript
connection reset by peer问题总结及解决方案
Oct 21 #Javascript
You might like
php解析url的三个示例
2014/01/20 PHP
PHP编程获取音频文件时长的方法【基于getid3类】
2017/04/20 PHP
php从数据库中获取数据用ajax传送到前台的方法
2018/08/20 PHP
PHP实现通过文本文件统计页面访问量功能示例
2019/02/13 PHP
javascript 支持ie和firefox杰奇翻页函数
2008/07/22 Javascript
通过隐藏option实现select的联动效果
2009/11/10 Javascript
IE6中使用position导致页面变形的解决方案(js代码)
2011/01/09 Javascript
File, FileReader 和 Ajax 文件上传实例分析(php)
2011/04/27 Javascript
js控制滚动条缓慢滚动到顶部实现代码
2013/03/20 Javascript
Extjs407 getValue()和getRawValue()区别介绍
2013/05/21 Javascript
jquery.qrcode在线生成二维码使用示例
2013/08/21 Javascript
AngularJS入门教程之Hello World!
2014/12/06 Javascript
chrome浏览器当表单自动填充时如何去除浏览器自动添加的默认样式
2015/10/09 Javascript
React.js入门学习第一篇
2016/03/30 Javascript
Angular 页面跳转时传参问题
2016/08/01 Javascript
vue2.0实战之基础入门(1)
2017/03/27 Javascript
基于BootStrap的文本编辑器组件Summernote
2017/10/27 Javascript
Javascript 类型转换、封闭函数及常见内置对象操作示例
2019/11/15 Javascript
js实现无缝轮播图特效
2020/05/09 Javascript
[01:25]2015国际邀请赛最佳短片奖——斧王《拆塔英雄:天赋异禀》
2015/09/22 DOTA
对pandas的dataframe绘图并保存的实现方法
2017/08/05 Python
Python 逐行分割大txt文件的方法
2017/10/10 Python
python机器学习实战之最近邻kNN分类器
2017/12/20 Python
Tensorflow之Saver的用法详解
2018/04/23 Python
OpenCV2从摄像头获取帧并写入视频文件的方法
2018/08/03 Python
python ipset管理 增删白名单的方法
2019/01/14 Python
Python3自定义http/https请求拦截mitmproxy脚本实例
2020/05/11 Python
Tensorflow--取tensorf指定列的操作方式
2020/06/30 Python
给Django Admin添加验证码和多次登录尝试限制的实现
2020/07/26 Python
Django-celery-beat动态添加周期性任务实现过程解析
2020/11/26 Python
Hotels.com泰国:酒店预订网站
2019/11/20 全球购物
留学推荐信写作指南
2014/01/25 职场文书
厨房领班竞聘演讲稿
2014/04/23 职场文书
爱牙日宣传活动总结
2015/02/05 职场文书
投标单位介绍信
2015/05/05 职场文书
css3带你实现3D转换效果
2022/02/24 HTML / CSS