双向绑定几乎完全限于使用ng-model
的元素。从视图到模型的方向使用标准事件处理程序来检测必须在模型内更新的更改(例如,onchange
)。在$digest
期间更新从模型返回到视图的方向。但我们不直接拨打$digest
。
页面上的每个要响应摘要循环的元素都会在某处使用$watch
将侦听器和表达式附加到其范围。当您编写{{ foo() }}
或当您使用ng-model='user.name'
时,内部会有一个以您的名义致电$watch
的Javascript表达式,该表达式将在每次运行摘要循环时运行。这种注册可能发生在模板的编译过程中(我们的第一个例子),或者可能发生在指令的链接阶段(我们的第二个例子)。
这里没有魔法。附加的监听器是常规函数 - 在我们的示例中,为您提供了表达式foo()
的监听器,它将更新页面上的html文本,而表达式user.name
的监听器将调用setText
或setOption
,或者ng-model
已附加的特定输入所需的任何内容。
尽管angular可以处理大部分的聆听,但您可以在任何可以访问范围的函数中手动附加您自己的监听器表达式(范围非常重要,因为如果相应部分该页面被删除)。注意多余。绑定不是免费的,绑定的东西越多,页面响应越慢。一次性绑定是降低成本的一种方法。使用$on
与$emit
和$broadcast
是另一个。
那么什么时候被摘要叫?这当然不是自动的。如果摘要循环正在运行,则表示有人在其作用域或根作用域上的某个地方称为$apply
。 ng-model
重视处理程序,它将响应常规的html事件,并将以您的名义致电$apply
。但另一方面,foo()
将永远不会被调用,直到其他位的脚本某处调用$apply
。幸运的是,您填写的大部分函数都是通过调用$apply
来包装这些函数的,因此您不必经常自己拨打电话(例如,$timeout
包装为$apply
,这就是我们用它代替setTimeout
的原因) 。但是,如果您使用的是角度范围以外的事物(连接到事件的第三方库),则需要记得自己拨打$apply
,并且就像上面一样,您可以通过在有权访问的任何地方拨打$apply
来手动执行此操作到一个范围。