我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。
我知道的唯一方法是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,错误如下:
错误:$digest已在进行中
有人知道如何避免这种错误或以不同的方式实现相同的事情吗?
我发现自从在angular中构建应用程序以来,我越来越需要手动将页面更新到我的范围。
我知道的唯一方法是从控制器和指令的范围调用$apply()。这样做的问题是,它不断向控制台抛出一个错误,错误如下:
错误:$digest已在进行中
有人知道如何避免这种错误或以不同的方式实现相同的事情吗?
当前回答
我已经能够通过在我知道$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。
其他回答
我对第三方脚本也有同样的问题,比如CodeMirror和Krpano,甚至使用这里提到的safeApply方法也没有为我解决错误。
但解决这个问题的方法是使用$timeout服务(不要忘记先注入它)。
因此,类似于:
$timeout(function() {
// run my code safely here
})
如果在代码中使用
这
也许是因为它在工厂指令的控制器内,或者只是需要某种绑定,那么您可以执行以下操作:
.factory('myClass', [
'$timeout',
function($timeout) {
var myClass = function() {};
myClass.prototype.surprise = function() {
// Do something suprising! :D
};
myClass.prototype.beAmazing = function() {
// Here 'this' referes to the current instance of myClass
$timeout(angular.bind(this, function() {
// Run my code safely here and this is not undefined but
// the same as outside of this anonymous function
this.surprise();
}));
}
return new myClass();
}]
)
看见http://docs.angularjs.org/error/$rootScope:inprog
当对$apply的调用有时在Angular代码外部异步运行(当应使用$apply时),有时在Angle代码内部同步运行(这导致$digest已经在进行中)时,就会出现问题。
例如,当您拥有一个从服务器异步获取项目并缓存它们的库时,可能会发生这种情况。第一次请求项时,将异步检索该项,以免阻止代码执行。然而,第二次,该项已在缓存中,因此可以同步检索。
防止此错误的方法是确保调用$apply的代码异步运行。这可以通过在调用$timeout时运行代码来完成,延迟设置为0(这是默认值)。然而,在$timeout内调用代码就不需要调用$apply,因为$timeout将自行触发另一个$digest循环,从而执行所有必要的更新等。
解决方案
简而言之,不要这样做:
... your controller code...
$http.get('some/url', function(data){
$scope.$apply(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
执行以下操作:
... your controller code...
$http.get('some/url', function(data){
$timeout(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
只有当您知道运行$apply的代码将始终在Angular代码之外运行时,才调用$apply(例如,您对$apply进行的调用将发生在由Angular代码以外的代码调用的回调内)。
除非有人意识到使用$timeout而不是$apply的一些不利影响,否则我不明白为什么不能总是使用$timeout(零延迟)而不是$app,因为它会做大致相同的事情。
如果你这样做,有时你仍然会出错(https://stackoverflow.com/a/12859093/801426).
试试看:
if(! $rootScope.$root.$$phase) {
...
使用$scope$$阶段||$scope$apply();相反
我建议您使用自定义事件,而不是触发摘要循环。
我发现,广播自定义事件并为此事件注册侦听器是一个很好的解决方案,无论您是否处于摘要周期,都可以触发您希望发生的操作。
通过创建自定义事件,您的代码也会更高效,因为您只触发订阅所述事件的侦听器,而不会像调用作用域时那样触发绑定到作用域的所有监视$申请
$scope.$on('customEventName', function (optionalCustomEventArguments) {
//TODO: Respond to event
});
$scope.$broadcast('customEventName', optionalCustomEventArguments);