AngularJS $ watch(),$ digest()和$ apply()
AngularJS的$ scope函数,$ watch(),$ digest()和$ apply()是AngularJS的一些主要功能。为了理解AngularJS,理解$ watch()
,$ digest()
和$ apply()
是必不可少的。
当我们从视图中的某处创建数据绑定到$ scope对象上的变量时,AngularJS在内部创建一个"监视"。监视意味着AngularJS监视$ scope对象上变量的变化。该框架正在"监视"变量。手表是使用$ scope。$ watch()函数创建的,我将在本文后面介绍。
在应用程序的关键点,AngularJS调用了$ scope。$ digest()函数。此函数遍历所有监视,并检查任何监视变量是否已更改。如果监视的变量已更改,则将调用相应的侦听器函数。侦听器功能会执行所需的任何工作,例如更改HTML文本以反映监视变量的新值。因此,$ digest()函数是触发数据绑定更新的函数。
大多数时候,AngularJS会为我们调用$ scope。$ watch()和$ scope。$ digest()函数,但是在某些情况下,我们可能必须自己调用它们。因此,了解它们的工作原理真的很好。
$ scope。$ apply()函数用于执行一些代码,然后再调用$ scope。$ digest(),以便检查所有手表并调用相应的手表侦听器函数。当将AngularJS与其他代码集成时,$ apply()函数非常有用。
在本文的其余部分中,我将更详细地介绍$ watch(),$ digest()和$ apply()函数。
$ watch()
$ scope.watch()函数创建一些变量的监视。注册手表时,会将两个函数作为参数传递给$ watch()函数:
- 值函数
- 监听器listener函数
这是一个例子:
$scope.$watch(function() {}, function() {} );
第一个功能是值功能,第二个功能是侦听器功能。
值函数应返回正在监视的值。然后,AngularJS可以对照watch函数最后一次返回的值来检查返回的值。这样,AngularJS可以确定值是否已更改。这是一个例子:
$scope.$watch(function(scope) { return scope.data.myVar }, function() {} );
这个示例valule函数返回$ scope变量scope.data.myVar。如果此变量的值更改,将返回不同的值,并且AngularJS将调用侦听器函数。
注意value函数如何将" scope"作为参数(名称中没有" $")。通过此参数,值函数可以访问$ scope及其变量。如果需要,值函数也可以监视全局变量,但是大多数情况下,我们将监视$ scope变量。
如果值已更改,则侦听器函数应执行所需的任何操作。也许我们需要更改另一个变量的内容,或者设置HTML元素或者其他内容的内容。这是一个例子:
$scope.$watch(function(scope) { return scope.data.myVar }, function(newValue, oldValue) { document.getElementById("").innerHTML = "" + newValue + ""; } );
本示例将HTML元素的内部HTML设置为变量的新值,并嵌入在使该值变为粗体的b
元素中。当然,我们可以使用代码" {{data.myVar}"来完成此操作,但这只是我们可以在侦听器函数中执行的操作的一个示例。
$ digest()
$ scope。$ digest()函数遍历$ scope对象中的所有手表及其子对象$ scope(如果有)。当$ digest()遍历手表时,它将为每个手表调用value函数。如果value函数返回的值与上次调用它返回的值不同,则调用该手表的侦听器函数。
只要AngularJS认为有必要,就会调用$ digest()函数。例如,在执行按钮单击处理程序之后,或者在AJAX调用返回之后(在执行了done()
/fail()
回调函数之后)。
我们可能会遇到一些极端的情况,其中AngularJS不会为我们调用$ digest()函数。通常,我们会通过注意到数据绑定不会更新显示的值来检测到这一点。在这种情况下,调用$ scope。$ digest()
它将起作用。或者,我们可以使用$ scope。$ apply()代替,我将在下一部分中进行解释。
$ apply()
$ scope。$ apply()函数将函数作为参数执行,然后在内部调用$ scope。$ digest()。这使我们更容易确保检查所有手表,从而刷新所有数据绑定。这是一个$ apply()示例:
$scope.$apply(function() { $scope.data.myVar = "Another value"; });
作为参数传递给$ apply()函数的函数将改变$ scope.data.myVar的值。当该函数退出时,AngularJS将调用$ scope。$ digest()
函数,以便检查所有手表,以观察值的变化。
例子
为了说明$ watch(),$ digest()和$ apply()的工作方式,请看以下示例:
<div ng-controller="myController"> {{data.time}} <br/> <button ng-click="updateTime()">update time - ng-click</button> <button id="updateTimeButton" >update time</button> </div> <script> var module = angular.module("myapp", []); var myController1 = module.controller("myController", function($scope) { $scope.data = { time : new Date() }; $scope.updateTime = function() { $scope.data.time = new Date(); } document.getElementById("updateTimeButton") .addEventListener('click', function() { console.log("update time clicked"); $scope.data.time = new Date(); }); }); </script>
本示例将$ scope.data.time变量绑定到插值指令,该指令将变量值合并到HTML页面中。这个绑定在$ scope.data.time
变量内部创建一个监视。
该示例还包含两个按钮。第一个按钮具有一个" ng-click"监听器。单击该按钮后,将调用$ scope.updateTime()函数,然后AngularJS调用$ scope。$ digest()以便更新数据绑定。
第二个按钮从控制器函数内部获得一个添加的标准JavaScript事件侦听器。单击第二个按钮时,将执行侦听器功能。如我们所见,两个按钮的侦听器功能几乎相同,但是调用第二个按钮的侦听器功能时,数据绑定不会更新。这是因为在执行第二个按钮的事件侦听器后未调用$ scope。$ digest()。因此,如果我们单击第二个按钮,则时间将在$ scope.data.time变量中更新,但永远不会显示新时间。
为了解决这个问题,我们可以在按钮事件监听器的最后一行添加$ scope。$ digest()
调用,如下所示:
document.getElementById("updateTimeButton") .addEventListener('click', function() { console.log("update time clicked"); $scope.data.time = new Date(); $scope.$digest(); });
除了在按钮监听器函数中调用$ digest()
之外,我们还可以这样使用$ apply()
函数:
document.getElementById("updateTimeButton") .addEventListener('click', function() { $scope.$apply(function() { console.log("update time clicked"); $scope.data.time = new Date(); }); });
注意如何从按钮事件侦听器内部调用$ scope。$ apply()函数,以及如何在作为参数传递给$ apply的函数中执行$ scope.data.time变量的更新。当$ apply()函数调用完成时,AngularJS在内部调用$ digest(),因此所有数据绑定都将更新。