AngularJS允许您实现双向数据绑定。然而,有趣的部分是它如何检测模型变化?该模型通常是一个普通的对象,如下面的代码。我们可以更改$scope.user
的名称属性,但AngularJS如何检测模型已更改? AngularJS是否枚举了$scope
对象的所有属性?
angular.module('myApp', [])
.controller('BusinessCardController', function($scope){
$scope.user = {
name: 'Tanay Pant'
}
});
<input type="text" ng-model="user.name" placeholder="Full Name" />
答案 0 :(得分:12)
有一个摘要周期,其中范围检查所有$ watch表达式并将它们与之前的值进行比较。它查看对象模型的变化,如果旧值与新值不同,AngularJS将更新相应的位置,a.k.a脏检查。
为了执行摘要周期$apply(fn)
必须运行,这就是你从JavaScript进入Angular世界的方法。如何调用$apply(fn)
(取自AngularJs integration with browser):
为了实现双向绑定,指令注册观察者。为了使页面快速有效,我们需要尝试减少我们创建的所有这些观察者。所以在使用双向绑定时应该小心 - 即只在你真正需要时才使用它。否则使用单向:
<h1> {{ ::vm.title }} </h1>
很明显,当用户在页面上时,页面标题可能不会被更改 - 或者如果更改,则需要查看新标题。因此,我们可以使用::
在模板链接阶段注册单向绑定。
我观察到的观察者爆炸的主要问题是有数百行的网格。如果这些行有很多列,并且在每个单元格中都有双向数据绑定,那么您就可以进行处理了。您可以在调制解调器时间坐下来等待页面加载!
答案 1 :(得分:7)
双向绑定几乎仅限于使用ng-model
的元素。从视图到模型的方向使用标准事件处理程序来检测必须在模型中更新的更改(例如,onchange
)。从模型返回到视图的方向在$digest
期间更新。但我们不直接致电$digest
。
页面上将响应摘要周期的每个元素都会在某处使用$watch
将侦听器和表达式附加到其作用域。当您编写{{ foo() }}
或使用ng-model='user.name'
时,会在内部调用$watch
代表您使用Javascript表达式,该表达式将在每次运行摘要周期时运行。这种注册可能发生在模板编译期间(我们的第一个例子),或者可能发生在指令的链接阶段(我们的第二个)。
这里没有魔力。附加的侦听器是常规函数 - 在我们的示例中,为您提供了表达式foo()
的侦听器,它将更新页面上的html文本,而表达式{{1}的侦听器}将调用user.name
或setText
,或setOption
所附加的特定输入所需的任何内容。
虽然angular可以处理大部分的监听,但是你可以在任何有权访问范围的函数中手动将自己的监听表连接到你自己的监听器(范围很重要,因为如果相应的部分,我们将拆除那些观察者页面被删除)。注意多余。绑定不是免费的,绑定的东西越多,页面响应的速度就越慢。一次性绑定是降低此成本的一种方法。将ng-model
与$on
和$emit
一起使用是另一种。
那么什么时候被称为消化?这当然不是自动的。如果摘要周期正在运行,则意味着某人在其作用域或根作用域上调用了$broadcast
。 $apply
附加处理程序,这些处理程序将响应常规的html事件,并代表您拨打ng-model
。但另一方面,$apply
永远不会被调用,直到某个地方的某些脚本调用foo()
。幸运的是,您填写的大多数函数都会通过调用$apply
来填充这些函数,因此您不必经常自己进行调用(例如,$apply
包含{$timeout
1}},这就是我们使用它而不是$apply
)的原因。但如果您使用的是角度范围之外的东西(连接到事件的第三方库),您需要记住自己调用setTimeout
,就像上面一样,您可以通过调用{ {1}}您可以访问范围的任何地方。
答案 2 :(得分:1)
为了使数据绑定成为可能,AngularJS使用$ watch API来观察范围的变化。 AngularJS为范围内的每个变量注册了观察者,以观察其中的值。如果范围上的变量值发生变化,则视图会自动更新。
这是因为触发了$ digest循环。因此,AngularJS处理当前范围内的所有已注册观察者和子节点并检查更新并调用专用观察者侦听器,直到模型稳定并且不再触发侦听器。 $ digest循环完成执行后,浏览器会重新呈现DOM并反映更改
默认情况下,范围内的每个变量都由角度观察。以这种方式,通过耗时的角度也观察到不必要的变量,结果页面变得缓慢。