我知道这两者Watchers
都是在 AngularJS 发生变化时Observers
立即计算的。$scope
但无法理解两者之间究竟有什么区别。
我最初的理解是Observers
为角度表达式计算的,这些表达式是在Watchers
执行$scope.$watch()
函数时执行的 HTML 端的条件。我在想正确吗?
我知道这两者Watchers
都是在 AngularJS 发生变化时Observers
立即计算的。$scope
但无法理解两者之间究竟有什么区别。
我最初的理解是Observers
为角度表达式计算的,这些表达式是在Watchers
执行$scope.$watch()
函数时执行的 HTML 端的条件。我在想正确吗?
$observe()是Attributes对象上的一个方法,因此,它只能用于观察/观察 DOM 属性的值变化。它仅在指令内部使用/调用。当您需要观察/观察包含插值的 DOM 属性(即 {{}} 的)时,请使用 $observe。
例如,attr1="Name: {{name}}"
, 然后在指令中:attrs.$observe('attr1', ...)
.
(如果你尝试scope.$watch(attrs.attr1, ...)
它不会工作,因为 {{}}s - 你会得到undefined
。)使用 $watch 做其他事情。
$watch()更复杂。它可以观察/观察“表达式”,其中表达式可以是函数或字符串。如果表达式是字符串,则将其$parse 'd(即,评估为Angular 表达式)到函数中。(每个摘要循环都会调用此函数。)字符串表达式不能包含 {{}}。$watch 是Scope对象上的一个方法,因此可以在您可以访问范围对象的任何地方使用/调用它,因此在
因为字符串被评估为 Angular 表达式,所以当你想观察/观察模型/范围属性时,经常使用 $watch。例如,attr1="myModel.some_prop"
,然后在控制器或链接函数中:scope.$watch('myModel.some_prop', ...)
或scope.$watch(attrs.attr1, ...)
(或scope.$watch(attrs['attr1'], ...)
)。
(如果你尝试attrs.$observe('attr1')
你会得到 string myModel.some_prop
,这可能不是你想要的。)
正如对@PrimosK 答案的评论中所讨论的,所有 $observes 和 $watches 都会在每个摘要循环中检查。
具有隔离范围的指令更复杂。如果使用 '@' 语法,您可以 $observe或 $watch包含插值的 DOM 属性(即 {{}} 的)。(它与 $watch 一起工作的原因是因为 '@' 语法为我们做了插值,因此 $watch 看到一个没有 {{}} 的字符串。)为了更容易记住何时使用哪个,我建议使用$observe 也适用于这种情况。
为了帮助测试所有这些,我编写了一个定义两个指令的Plunker 。一个 ( d1
) 不创建新范围,另一个 ( d2
) 创建隔离范围。每个指令都有相同的六个属性。每个属性都是 $observe'd 和 $watch'ed。
<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
attr5="a_string" attr6="{{1+aNumber}}"></div>
查看控制台日志,可以看到 $observe 和 $watch 在链接函数中的区别。然后单击链接,查看单击处理程序所做的属性更改触发了哪些 $observes 和 $watches。
请注意,当链接函数运行时,尚未评估任何包含 {{}} 的属性(因此,如果您尝试检查属性,您将得到undefined
)。查看插值的唯一方法是使用 $observe(如果使用带有 '@' 的隔离范围,则使用 $watch)。因此,获取这些属性的值是一个异步操作。(这就是我们需要 $observe 和 $watch 函数的原因。)
有时你不需要$observe 或$watch。例如,如果您的属性包含一个数字或一个布尔值(不是字符串),只需评估一次:attr1="22"
,然后在您的链接函数中:var count = scope.$eval(attrs.attr1)
。如果它只是一个常量字符串——attr1="my string"
那么只需attrs.attr1
在你的指令中使用(不需要 $eval())。
另请参阅Vojta关于 $watch 表达式的 google 群组帖子。
如果我正确理解您的问题,您会问如果您使用注册侦听器回调$watch
或使用$observe
.
注册的回调在执行$watch
时被触发$digest
。
$observe
当包含插值的属性的值更改时调用注册的回调(例如attr="{{notJetInterpolated}}"
)。
在指令内部,您可以以非常相似的方式使用它们:
attrs.$observe('attrYouWatch', function() {
// body
});
或者
scope.$watch(attrs['attrYouWatch'], function() {
// body
});
我认为这很明显:
请记住:两个函数都有两个参数,
$observe/$watch(value : string, callback : function);
function (oldValue, newValue)
我已经做了一个plunker,所以你实际上可以掌握它们的使用情况。我使用了变色龙的类比来使其更容易描绘。
为什么 $observe 与 $watch 不同?
watchExpression 在每个 digest() 循环中被评估并与前一个值进行比较,如果 watchExpression 值发生变化,则调用 watch 函数。
$observe 专门用于观察插值。如果指令的属性值被插值,例如dir-attr="{{ scopeVar }}"
,则仅在设置插值时调用观察函数(因此当 $digest 已经确定需要进行更新时)。基本上已经有一个插值的观察者,并且 $observe 函数捎带了它。