在 Angular 中什么被认为是“观察者”?观察者本身是观察者的唯一类型,还是其他 Angular 构造(如ngModel
观察者)也是如此?
还是我错过了大局?例如,观察者是什么使指令喜欢ngModel
工作?
更新:有没有办法判断何时存在观察者?在测试中我想知道什么时候调用 scope.$digest()
在 Angular 中什么被认为是“观察者”?观察者本身是观察者的唯一类型,还是其他 Angular 构造(如ngModel
观察者)也是如此?
还是我错过了大局?例如,观察者是什么使指令喜欢ngModel
工作?
更新:有没有办法判断何时存在观察者?在测试中我想知道什么时候调用 scope.$digest()
watchers
只不过是脏检查,它跟踪旧值和新值
他们在每个摘要周期都得到评估。它可以是范围变量或任何表达式的组合。Angular 确实在每个摘要循环中收集所有这些观察者并将其维护在$$watchers
数组中。console.log($scope.$watchers)
您可以通过在控制器内部进行操作来查看有多少观察者。
标记
<body class="container" ng-controller="myCtrl">
Hello {{test}}
I'm test to show $$watchers {{test1}}
<div ng-show="show">SHowiing this div using ng-show</div>
<div ng-if="show">SHowiing this div using ng-show</div>
Watcher is not compulsary that should scope variables {{'1'}}
</body>
在上面的代码中猜测有多少观察者,你可以看到有 3 个{{}}
插值指令将放置在观察者数组中,然后如果你在控制台中看到$scope.$$watchers
它将显示 5 个观察者。
它怎么会显示 5 个观察者。正如我们只能看到 3,实际上我们已经使用了ng-show
andng-if
指令,它在内部放置$watch
在其属性值中提供的表达式上。& 这些表达式会在每个摘要循环中进行评估。
您还可以使用
$watch
(deep/simple watch) &创建自定义观察者$watchGroup
你也可以让 watcher use $attrs.$observe
,它的工作方式与 watch 相同,但它唯一的特殊之处在于它适用于插值变量。
$attrs.$observe('test',function(value){
alert('')
});
大多数角度指令在内部使用观察器,如ng-repeat
, ng-show
, ng-if
, ng-include
, ng-switch
, ng-bind
, 插值指令{{}}
, 过滤器等。它们在内部放置观察器来管理两种方式绑定的东西。
观察者(如果我们只采用您所基于的文档)是角度机制,旨在以双向绑定样式在任何角度摘要周期中观察变量或函数结果;不管摘要循环的触发事件是什么。
我将任何能够根据可能发生的任何事件触发某些代码的 Angular 机制称为“观察者”。
通常,要创建 Watcher,您应该使用:$scope.watch(...)
请注意,最好尽可能避免观察者。
实际上,它们的回调将在每个摘要周期触发以执行脏检查;经常影响性能。
ng-model
与观察者的概念无关。
ng-model
只是一种将某些变量从视图绑定到控制器的方法。
它们是两个截然不同的概念。
为了说明 $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() 函数是如何从按钮事件侦听器内部调用的,以及 $scope.data.time 变量的更新是如何在作为参数传递给 $apply() 函数的函数内部执行的。当 $apply() 函数调用完成 AngularJS 在内部调用 $digest() 时,所有数据绑定都会更新。