我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。
我知道的唯一方法是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,错误如下:
错误:$digest已在进行中
有人知道如何避免这种错误或以不同的方式实现相同的事情吗?
我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。
我知道的唯一方法是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,错误如下:
错误:$digest已在进行中
有人知道如何避免这种错误或以不同的方式实现相同的事情吗?
当前回答
使用$scope$$阶段||$scope$apply();相反
其他回答
保持此过程干燥的简便小助手方法:
function safeApply(scope, fn) {
(scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}
这是我的utils服务:
angular.module('myApp', []).service('Utils', function Utils($timeout) {
var Super = this;
this.doWhenReady = function(scope, callback, args) {
if(!scope.$$phase) {
if (args instanceof Array)
callback.apply(scope, Array.prototype.slice.call(args))
else
callback();
}
else {
$timeout(function() {
Super.doWhenReady(scope, callback, args);
}, 250);
}
};
});
这是它的用法示例:
angular.module('myApp').controller('MyCtrl', function ($scope, Utils) {
$scope.foo = function() {
// some code here . . .
};
Utils.doWhenReady($scope, $scope.foo);
$scope.fooWithParams = function(p1, p2) {
// some code here . . .
};
Utils.doWhenReady($scope, $scope.fooWithParams, ['value1', 'value2']);
};
您应该根据上下文使用$evalAsync或$timeout。
这是一个有很好解释的链接:
http://www.bennadel.com/blog/2605-scope-evalasync-vs-timeout-in-angularjs.htm
我已经能够通过在我知道$digest函数将运行的地方调用$eval而不是$apply来解决这个问题。
根据文档,$apply基本上做到了这一点:
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
在我的例子中,ng单击会更改范围内的变量,而该变量上的$watch会更改必须应用$apply的其他变量。最后一步导致错误“摘要已在进行中”。
通过在watch表达式中将$apply替换为$eval,作用域变量将按预期更新。
因此,如果因为Angular中的一些其他变化,digest将以任何方式运行,那么您需要做的就是$eval‘ing。
摘要周期是一个同步调用。在完成之前,它不会将控制权交给浏览器的事件循环。有几种方法可以解决这个问题。处理此问题的最简单方法是使用内置的$timeout,第二种方法是如果您使用下划线或lodash(您应该使用),请调用以下命令:
$timeout(function(){
//any code in here will automatically have an apply run afterwards
});
或者如果你有lodash:
_.defer(function(){$scope.$apply();});
我们尝试了几种变通方法,我们讨厌将$rootScope注入所有控制器、指令,甚至一些工厂。因此,到目前为止,$timeout和_.defer一直是我们的最爱。这些方法成功地告诉angular等待下一个动画循环,这将保证当前范围$申请已结束。