Link directives after asynchronous calls in AngularJS

在许多场景中,需要等待异步的请求完成后对DOM进行操作。例如,在异步获取图片数据后,对图片进行等比缩放。对于DOM的操作通常放置在directive中,而当数据是异步请求的时候,DOM的数据并未就绪,此时在directive中去操作时会无法找到数据进而导致操作失败。

考虑对图片进行等比缩放的场景,假定将图片缩放的操作放置在image-resizedirective中,图片的src等待异步请求后才能获取到。HTML代码如下:

1
<img ng-src="image.src" alt="image.alt" image-resize />

ng-src绑定到image.src上,在此数据变化时就可以判定获取图片地址的异步请求已经完成。所以,在image-resize中通过监视ng-src这个属性,当变化的时候就进行缩放操作:

1
2
3
4
5
6
7
8
9
10
11
angular.module('directives', [])
.directive('imageResize', [function () {
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
$attrs.$observe('ngSrc', function () {
// Resize image.
});
}
};
}]);

由于对于图片的操作仅需在获取到图片地址后执行一次,所以使用$attributes.$observe并不是一个上等的方案。如果能在异步执行完成,执行directive就无需进行监视操作。在AngularJS中,directive首先会被compile一次,并在第一次使用的时候进行link,而第一使用受类似ng-if等的限定。

也就是说,当一个DOM元素上的ng-if中表达式为false时,元素上的其他directive不会被link。使用这个特性,以上的代码可以简写:

1
2
<!-- 新增了ng-if,绑定到image.loaded变量上-->
<img ng-src="image.src" alt="image.alt" ng-if="image.loaded" image-resize />

同时image-resize内部对于ngSrc属性的监视也可以去掉:

1
2
3
4
5
6
7
8
9
10
angular.module('directives', [])
.directive('imageResize', [function () {
return {
restrict: 'A',
link: function ($scope, $element, $attrs) {
// 无需监视attr.ngSrc属性
// Resize image.
}
};
}]);

当异步请求完成时,将image.loaded变量设定为true,当AngularJS执行下一轮digest的时候,图片元素上的ng-if结果为true,触发resize-imagelink,此后即可以对图片进行后续的操作。

0%