$parse/$eval和$observe/$watch如何区分
大家在看angular的时候,有时候偶尔会看到$parse,$eval和$observe,$watch这两对语法,随着深入使用angular,就不可避免使用到它。
文章从内部运行机制跟实际需求的角度来解释这两对语法的区别。
原理
$parse和$eval
首先,$parse跟$eval都是用来解析表达式的, 但是$parse是作为一个单独的服务存在的。$eval是作为scope的方法来使用的。
$parse典型的使用是放在设置字符串表达式映射在真实对象上的值。也可以从$parse上直接获取到表达式对应的值。
var getter = $parse('user.name');
var setter = getter.assign;
setter(scope, 'new name');
getter(context, locals) // 传入作用域,返回值
setter(scope,'new name') // 修改映射在scope上的属性的值为‘new value’
$eval 即scope.$eval,是执行当前作用域下的表达式,如:scope.$eval('a+b'); 而这个里的a,b是来自 scope = {a: 2, b:3};
看看源码它的实现是
$eval: function(expr, locals) {...}
,
可以找到它也是基于$parse,不过它的参数已经被固定为this,就是当前的scope,所以$eval只是在$parse基础上的封装而已,是一种$parse快捷的API。
$observe和$watch
$observe和$watch都可以用来对属性进行监控的,先来看下$observe源码:
$observe: function(key, fn) {...}
)), listeners = ($$observers[...]
|| ($$observers[...]
= [...]
)); listeners.push(fn); $rootScope.$evalAsync(function() {...}
); }
}
); return fn; }
从这里可以看出来它是使用了$rootScope.$evalAsync()方法来监控的。什么是$evalAsync呢?是一个异步解析的操作,是在其他表达式都已经解析之后再解析,这样使它拥有了处理像插值字符串的机会。
$observe是属性对象上的方法,因此它是用来监控DOM属性上的值的变化,它仅用在指令内部,当你需要在指令内部监控包含有插值表达式的DOM属性的时候,就要用到这个方法,
比如,<code>attr1="Name:"</code>,然后在指令里面:<code>attrs.$observe('attr1', ....)</code>,但是假如你只用<code>scope.$watch(attrs.attr1,...)</code>,这种情况下是无效的,因为<code></code>无法被解析,所以你得到的是<code>undefined</code>, 在其他情况下用$watch。
$watch更复杂一点,它可以监视表达式,这个表达式可以是函数或者字符串,假如表达式是字符串的话,会被封装成一个函数,然后在digest循环的时候被调用。 这个字符串表达式不能包含<code></code>,$watch是一个scope对象上的方法,所以它可以在任何你可以访问到作用域的地方被调用。比如,控制器中或者link函数中。因为字符串是被当做angular的表达式解析的,所以$watch经常被用在当你想要监控一个模型或者作用域对象的时候,
比如:<code>attr1="myModel.some_prop"</code>,然后在控制器中或者link函数中<code>scope.$watch('myModel.some_prop',...)</code>或者<code>scope.$watch(attrs.attr1,...)</code>或者<code>scope.$watch(attrs['attr1'],...)</code>。假如你使用<code>attrs.$observe('attr1')</code>你就只能得到<code>myModel.some_prop</code>字符串的值。
$observe和$watch都会在每个digest阶段被执行。
带有隔离作用域的指令的情况会复杂点。假如'@'语法被使用的时候,你可以使用$observe或者$watch一个包含插值<code></code>的DOM属性,之所以$watch在这里也能用,是因为'@'语法为我们处理了,因此传给$watch是的时候是没有的字符串。这样子我们使用起来就更容易了。但是这种情况还是推荐使用$observe。
想要更好的理解,大家可以看这个例子<a href="http://plnkr.co/edit/HBha8sVdeCqhJtQghGxw?p=preview">Plunker</a>。
注意到,当link函数被执行的时候,任何包含的DOM属性都还没被解析,所以此时假如你检查这种方式定义的属性值的话,就是得到undefined, 唯一的方法可以得到内部的真实值的是使用$observe,或者在包含带有@语法的隔离作用域中使用$watch.因此获取这些属性的值是异步操作(因为要内部的值是链接到其他地方,所以要等这些值稳定之后,才能得到属性的正确值,而在上文的源码中你可以看到使用了$rootScope.$evalAsync())。
有时候我们并不需要$observe和$watch,比如你的属性一个boolen类型或者数字类型的值,只要解析他们一次就够了,你可以这么做:<code>attr1="22"</code>, 同时你的链接函数: <code>var count = scope.$eval(attrs.attr1)</code>,假如只是包含字符串,<code>attr1="my string"</code>,你就可以直接使用<code>attrs.attr1</code>而不需要用$eval方法。
使用场景
想要在沒有isolate scope的directive中取出foo的属性值,存在以下几种情况:
- foo属性值是固定的字串值,例如想要傳 class name,id 等。
<div foo="fadeOut"></div>
因为这种情况是直接給定固定字串值,可以直接在 foo directive 中的 link function 直接取出属性值,attrs.foo。
- foo 属性值是非字串的值,例如:boolean, number 等。
<div foo="true"></div>, <div foo="123"></div>
如果通过 attrs.foo 直接取值的话,就只能取到字符串值,但是我想要取值后就是正确的类型的話,可以使用 scope.$eval(attrs.foo)。
foo 属性是一個 scope property。
angular.controller('BarCtrl', function($scope) {...});
// html
<div ng-controller="BarCtrl">
<div foo="bar.test"></div>
</div>
如果想要在 foo directive 中去改变bar.test的值的話,可以使用$parse來取值,$parse(attrs.foo),$parse后会一個 function,之後你可以通过这个function的assign property 去设置 bar.test 的值:
var model = $parse(attrs.foo);
model.assign(scope, 'Hello world'); // scope 为 link function 的 scope
foo 属性值是 interpolated attribute。
<div foo="2"></div>, <div foo=""></div>
如果直接在 link function 直接使用 attrs.foo 取值的話,得到的是 undefined,因为此时 interpolated attribute 还没被解析,所以我們可以通过 $observe 來取值,attrs.$observe。
随机推荐
- 标准IO
标准IO由ISO C 标准的IO库,它处理了很多底层细节,比如合适的缓冲大小等等,因此更易于使用,但是也引入了一些其他问题. 流向 标准IO使用FILE对象关联流,流可以是面向宽字节的也可以是面向单字 ...
- Flex SDK包内文件夹内容说明
打开SDK目录,让我们看看SDK中都包含了哪些内容: ant包含Flex对Ant的支持库,JAR和Java源码都有,方便我们基于Ant完成对项目的构建 asdoc基于ASDOC我们可以很方便的生成代码 ...
- flex中文说明手册
http://help.adobe.com/zh_CN/Flex/4.0/UsingFlashBuilder/WS6f97d7caa66ef6eb1e63e3d11b6c4d0d21-7f07.htm ...
- 启动 Eclipse 弹出“Failed to load the JNI shared library jvm.dll”错误
原因1:给定目录下jvm.dll不存在. 对策:(1)重新安装jre或者jdk并配置好环境变量.(2)copy一个jvm.dll放在该目录下. 原因2:eclipse的版本与jre或者jdk版本不一致 ...
- dom 冒泡事件
<!doctype html> <html> <head> <meta charset="utf-8"> <style> ...
- LINQ标准查询操作符(三)——Aggregate、Average、Distinct、Except、Intersect、Union、Empty、DefaultIfEmpty、Range、Repeat
七.聚合操作符 聚合函数将在序列上执行特定的计算,并返回单个值,如计算给定序列平均值.最大值等.共有7种LINQ聚合查询操作符:Aggregate.Average.Count.LongCount.Ma ...
- 【转】大数据以及Hadoop相关概念介绍
原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4230220.html 感谢! 一.大数据的基本概念 1.1.什么是大数据 大数据指的就是要处理的数据是TB级别以 ...
- Spark生态系统BDAS
目前,Spark已经发展成为包含众多子项目的大数据计算平台. 伯克利将Spark的整个生态系统称为伯克利数据分析栈(BDAS). 其核心框架是Spark,同时BDAS涵盖支持结构化数据SQL查询与分析 ...
- ZOJ 3810 Pretty Poem 分类: ACM 2015-05-17 14:40 83人阅读 评论(0) 收藏
Pretty Poem Time Limit: 2 Seconds Memory Limit:65536 KB Poetry is a form of literature that uses ...
- VoToucher
VoToucher package com.isoftstone.pcis.policy.common.utils; import com.isoftstone.pcis.policy.common. ...