在进行一个老项目的开发中,引入的jQuery插件需要DOM操作,因为页面包裹在异步加载的ng-include中,尝试以下三种方式来保证DOM渲染完成
-
$timeout// $timeout接收三个参数,分别为要执行的函数,延迟(默认为0),是否要执行一次$digest循环(默认为true) $timeout(function () { // 在这里执行DOM加载完成后的操作 },0,true); -
事件监听
// 在onload时发送一个事件 <div ng-include="'/path/to/template.html'" onload="templateLoaded()" template-loaded></div> // 在controller中广播事件 app.controller('MyController', function($scope) { $scope.templateLoaded = function() { // 挂载时广播事件 $scope.$broadcast('TemplateLoadedEvent'); }; }); // 在directive中监听事件 app.directive('templateLoaded', function() { return { restrict: 'A', link: function(scope, element, attrs) { scope.$on('TemplateLoadedEvent', function() { // 在这里执行DOM加载完成后的操作 }); } }; }); -
$includeContentLoaded// $includeContentLoaded会在ng-include指令加载、编译并插入模板内容到 DOM 后触发 $scope.$on('$includeContentLoaded', function () { // 在这里执行DOM加载完成后的操作 });
上面几种方法仅对部分DOM元素有效,但均不能保证在ng-repeat内部的元素挂载完成后进行操作。因为ng-repeat与ng-include都是异步执行的,执行过程中可能会跨越多个$digest循环,而$timeout只确保在当前 $digest 循环之后、下一个事件循环之前执行。 因此$timeout内部的函数执行时,ng-include 的 AJAX 请求可能还没有返回,ng-repeat 中的列表项也可能未处理完成。同样,使用$evalAsync或$applyAsync也不能保证在ng-repeat内部的元素挂载完成后进行操作。 即使给$timeout加上延迟,也不是一个可靠的办法,因为延迟的时间难以估计,会受浏览器性能、客户端机器的性能、网络延迟等多方面的影响。
后面两种方法也只对ng-include有效,如果想保证ng-repeat也能够加载完成,可以使用 ng-repeat 提供的$last ,它是一个布尔值,用来指示当前迭代的元素是否是数组中的最后一个元素。代码如下
-
使用自定义指令
// ng-repeat最后一个元素渲染完毕后,发送事件 app.directive('RepeatFinished',function () { return { restrict: 'A', link: function (scope, element, attrs) { if (scope.$last === true) { //确保当前$digest循环结束 $timeout(function () { // 发送事件 scope.$emit('onRepeatFinished'); }); } }, }; }); // 在对应位置绑定指令 <div ng-repeat="item in items" on-finish-render></div> // 在controller中监听事件 app.controller('MyController', function($scope) { $scope.$on('ngRepeatFinished', function() { // 在这里执行DOM加载完成后的操作 }); }); -
ng-init//绑定ng-init对应的处理函数 <div ng-repeat="item in items" ng-init="initItem($last)"></div> // 在controller中处理 app.controller('MyController', function($scope) { $scope.initItem = function (isLast) { if (isLast) { //确保当前$digest循环结束 $timeout(function () { // 在这里执行DOM加载完成后的操作 }); } }; });
总结:尽量避免在AngularJS中进行DOM操作,对于相关的操作要小心处理