没有魔法或超时,Angular 理解某些东西(包括函数返回值)改变的方式是在每次摘要循环运行时执行 dirty-checking。要了解它是如何工作的,让我们看一下您的示例,看看 Angular 为您执行的步骤,以及您如何告诉 Angular 发生了一些变化。
第 1 步:创建手表
如果我们查看ngShowDirective的源代码,我们会看到以下内容
var ngShowDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngShow, function ngShowWatchAction(value){ // <= create watch
$animate[toBoolean(value) ? 'removeClass' : 'addClass'](element, 'ng-hide');
});
};
}];
scope.$watch() 的第一个参数是一个表达式,它取自 DOM 元素的 ng-show 属性,并编译为一个函数,该函数在监视值更改时返回 true。由于传递的是一个函数 - Angular 将运行它来获取新值。
第 2 步:订阅 DOM 事件并运行摘要循环
Angular 订阅了许多 DOM 事件,以便运行摘要循环并检查每次浏览器中发生某些事情时是否发生了某些变化。例如,让我们看看当浏览器触发 hashchange 事件时 Angular 做了什么:
$browser.onUrlChange(function(newUrl) {
if ($location.absUrl() != newUrl) {
$rootScope.$evalAsync(function() {
var oldUrl = $location.absUrl();
$location.$$parse(newUrl);
if ($rootScope.$broadcast('$locationChangeStart', newUrl,
oldUrl).defaultPrevented) {
$location.$$parse(oldUrl);
$browser.url(oldUrl);
} else {
afterLocationChange(oldUrl);
}
});
if (!$rootScope.$$phase) $rootScope.$digest(); // <= run digest loop if it is not already running
}
});
第 3 步:运行所有手表并在手表发生变化时调用回调
Scope.prototype = {
...
$digest: function() {
...
if ((value = watch.get(current)) !== (last = watch.last) &&
!(watch.eq
? equals(value, last)
: (typeof value == 'number' && typeof last == 'number'
&& isNaN(value) && isNaN(last)))) {
...
watch.fn(value, ((last === initWatchVal) ? value : last), current); // <= run watch callback function in case watch value has been changed
...
}
...
}
}
就是这样,Angular 就是这样知道何时让<ANY ng-show="aFunction()"> ... </ANY> 可见或不可见。
在最常见的情况下,Angular 会为您完成所有这些工作,但是在某些情况下,Angular 并不知道某些事情发生了变化。例如,当值在自动创建手表的 Angular 范围之外更改时。在这些情况下,您应该注意自己致电scope.$digest() 或scope.$apply()。最常见的情况之一是在指令的事件侦听器中更改范围属性值:
angular.module('demo').directive('clickable', function() {
return {
link: function(scope, element) {
element.bind('click', function() {
scope.$apply(function() { // <= call scope.$apply() in order to tell Angular that something is changed in the scope
scope.foo++;
});
});
}
}
});
除了$apply()调用$digest()之外,推荐使用$apply()带回调函数。这样 Angular 将知道在范围更改期间是否有问题,并在出现任何错误时处理错误。
希望它有助于理解 Angular 如何知道范围内的变化。
在 Angular 之外观察函数返回值
如果您想利用 Angular 在 Angular 应用程序代码之外观察函数返回值的功能,您可以执行以下操作:
- 获取应用程序的顶级元素
- 获取 Angular 的
$rootScope
- 使用
$watch()观察变化
示例:
var valueToBeChanged = 0,
rootElement = document.querySelector('[ng-app]'),
rootScope = angular.element(rootElement).scope();
function myFunc() {
return valueToBeChanged;
}
function myCallback() {
console.log('Value have been changed!');
}
rootScope.$watch(myFunc, myCallback);
angular.element(document.querySelector('body')).on('click', function() {
rootScope.$apply(function() {
valueToBeChanged++;
});
});
Plunker:http://plnkr.co/edit/LRK08EsP0jrOu9n42VwB?p=preview