`
张江涛
  • 浏览: 5309 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

$parse/$eval和$observe/$watch如何区分

阅读更多
$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) {
    return $parse(expr)(this, locals);
},
可以找到它也是基于$parse,不过它的参数已经被固定为this,就是当前的scope,所以$eval只是在$parse基础上的封装而已,是一种$parse快捷的API。

$observe和$watch
$observe和$watch都可以用来对属性进行监控的,先来看下$observe源码:

$observe: function(key, fn) {
    var attrs = this,
        $$observers = (attrs.$$observers || (attrs.$$observers = {})),
        listeners = ($$observers[key] || ($$observers[key] = []));

    listeners.push(fn);
    $rootScope.$evalAsync(function() {
      if (!listeners.$$inter) {
        // no one registered attribute interpolation function, so lets call it manually
        fn(attrs[key]);
      }
    });
    return fn;
}
从这里可以看出来它是使用了$rootScope.$evalAsync()方法来监控的。什么是$evalAsync呢?是一个异步解析的操作,是在其他表达式都已经解析之后再解析,这样使它拥有了处理像{{}}插值字符串的机会。

$observe是属性对象上的方法,因此它是用来监控DOM属性上的值的变化,它仅用在指令内部,当你需要在指令内部监控包含有插值表达式的DOM属性的时候,就要用到这个方法,

比如,<code>attr1="Name:{{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) {
$scope.bar = {
test: ''
};
});
// 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="{{1+1}}"></div>, <div foo="{{bar}}"></div>
如果直接在 link function 直接使用 attrs.foo 取值的話,得到的是 undefined,因为此时 interpolated attribute 还没被解析,所以我們可以通过 $observe 來取值,attrs.$observe。

文章引用
   http://www.ngnice.com/posts/2314014da4eea8
分享到:
评论

相关推荐

    AngularJs $parse、$eval和$observe、$watch详解

    $parse和$eval $parse和$eval这两个函数都可以解析表达式的值. 它们的区别在于$parse是一个服务, 可以注入使用. $eval是scope对象上的一个方法, 我们只能在能访问scope的场景下使用它. var getter = $parse('user...

    说说AngularJS中的$parse和$eval的用法

    本篇文章主要介绍了说说AngularJS中的$parse和$eval的用法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

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

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

    AngularJS中如何使用$parse或$eval在运行时对Scope变量赋值

    主要介绍了AngularJS中如何使用$parse或$eval在运行时对Scope变量赋值的相关资料,本文介绍的非常详细,具有参考借鉴价值,需要的朋友可以参考下

    EC-Webhook

    使用此URL和任何所需的有效负载配置第三方工具 当从Webhook触发过程或管道时,将运行该过程或管道并添加以下属性: webhookData webhookHeaders这些是JSON字符串,可以使用JSON JavaScript库进行解析: $[/...

    Angularjs 1.3 中的$parse实例代码

    本文通过实例代码给大家介绍了angularjs $parse的相关知识,非常不错,具有参考借鉴价值,需要的朋友参考下吧

    Angularjs手动解析表达式($parse)

    主要介绍了Angularjs手动解析表达式($parse)的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下

    PHP智能解析收货地址.pdf

    $parse['name'] = ''; $parse['mobile'] = ''; $parse['postcode'] = ''; $parse['idno'] = ''; $parse['detail'] = ''; //1. 过滤掉收货地址中的常⽤说明字符,排除⼲扰词 $search = ['收货地址', '地址', '收货⼈...

    parse-android-test-app:演示适用于Parse测试服务器的Android应用程序。 服务器

    散记 关于 演示Android应用程序,用于。 谷歌认证 资源 。 用法 在preferences.xml将此值替换为自己的类似物: [APP_ID]&lt;/ string&gt; Facebook身份验证 ...在preferences.xml将此值替换为自己的类似物: ...

    changelog:读取和写入变更日志

    解析类似markdown的更改日志(如我们的示例),解析出版本,部分,更改和参考。 动机:自动更新变更日志 与命令捆绑在一起的changelogger 。 changelogger命令 安装 $ go get github....

    php分页类很完美

    $parse_url=parse_url($url); $url_query=$parse_url["query"]; //单独取出URL的查询字串 if($url_query){ //因为URL中可能包含了页码信息,我们要把它去掉,以便加入新的页码信息。 //这里用到了正则表达式,请参考...

    pandoc-mediawiki-ext

    Pandoc Mediawiki扩展这是一个Mediawiki扩展,可以在运行时将其他文档格式转换为mediawiki。...用法声明PARSEFROM魔术字默认情况下,此扩展名不执行任何操作,除非您在页面上添加特殊的魔术字。 在页面

    js比较日期大小的方法

    本文实例讲述了js比较日期大小的方法。分享给大家供大家参考。具体如下: function DateDiff(d1,d2){ ... start_at = new Date(d1.replace(/^(\d{4})(\d{2})(\d{2})$/,$1/$2/$3)); end_at = new Date(d2.replace(/^

    php 数据库分页显示

    //php 数据库分页显示,...select * from $db_table limit ($page-1)*$pagesize,$pagesize //解释一下 $db_table 数据表名 $page 当前页 $pagesize 每页显示多少条信息 //parse_url() URL解析成固 有键值名称的数组

Global site tag (gtag.js) - Google Analytics