本文翻译自:AngularJS : Prevent error $digest already in progress when calling $scope.$apply()
I'm finding that I need to update my page to my scope manually more and more since building an application in angular. 我发现自从以角度构建应用程序后,我需要越来越多地手动将页面更新到我的范围。
The only way I know of to do this is to call $apply()
from the scope of my controllers and directives. 我知道这样做的唯一方法是从我的控制器和指令的范围调用$apply()
。 The problem with this is that it keeps throwing an error to the console that reads : 这个问题是它不断向控制台抛出一个错误:
Error: $digest already in progress 错误:$ digest已在进行中
Does anyone know how to avoid this error or achieve the same thing but in a different way? 有谁知道如何避免这个错误或以不同的方式实现相同的事情?
参考:https://stackoom.com/question/rPQQ/AngularJS-在调用-scope时防止错误-digest正在进行中-apply
When you get this error, it basically means that it's already in the process of updating your view. 当您收到此错误时,它基本上意味着它已经在更新您的视图。 You really shouldn't need to call $apply()
within your controller. 你真的不需要在你的控制器中调用$apply()
。 If your view isn't updating as you would expect, and then you get this error after calling $apply()
, it most likely means you're not updating the the model correctly. 如果您的视图没有按预期更新,然后在调用$apply()
后出现此错误,则很可能意味着您没有正确更新模型。 If you post some specifics, we could figure out the core problem. 如果你发布一些细节,我们可以找出核心问题。
Don't use this pattern - This will end up causing more errors than it solves. 不要使用这种模式 - 这最终会导致比它解决的更多错误。 Even though you think it fixed something, it didn't. 即使你认为它固定了一些东西,它也没有。
You can check if a $digest
is already in progress by checking $scope.$$phase
. 您可以通过检查$scope.$$phase
来检查$scope.$$phase
$digest
是否已在进行中。
if(!$scope.$$phase) {
//$digest or $apply
}
$scope.$$phase
will return "$digest"
or "$apply"
if a $digest
or $apply
is in progress. 如果$digest
或$apply
正在进行中, $scope.$$phase
将返回"$digest"
或"$apply"
。 I believe the difference between these states is that $digest
will process the watches of the current scope and its children, and $apply
will process the watchers of all scopes. 我相信这些状态之间的区别在于$digest
将处理当前范围及其子项的监视, $apply
将处理所有范围的观察者。
To @dnc253's point, if you find yourself calling $digest
or $apply
frequently, you may be doing it wrong. 对于@ dnc253来说,如果你发现自己经常叫$digest
或$apply
,你可能做错了。 I generally find I need to digest when I need to update the scope's state as a result of a DOM event firing outside the reach of Angular. 我通常发现当需要更新范围的状态时,我需要消化,因为DOM事件在Angular的范围之外触发。 For example, when a twitter bootstrap modal becomes hidden. 例如,当twitter引导模式变为隐藏时。 Sometimes the DOM event fires when a $digest
is in progress, sometimes not. 有时,当$digest
正在进行时,DOM事件会触发,有时则不会。 That's why I use this check. 这就是我使用这张支票的原因。
I would love to know a better way if anyone knows one. 如果有人知道,我很想知道一个更好的方法。
From comments: by @anddoutoi 来自评论:@anddoutoi
angular.js Anti Patterns angular.js反模式
- Don't do
if (!$scope.$$phase) $scope.$apply()
, it means your$scope.$apply()
isn't high enough in the call stack. 不要做if (!$scope.$$phase) $scope.$apply()
,这意味着你的$scope.$apply()
在调用堆栈中不够高。
Sometimes you will still get errors if you use this way ( https://stackoverflow.com/a/12859093/801426 ). 如果您使用这种方式,有时您仍会遇到错误( https://stackoverflow.com/a/12859093/801426 )。
Try this: 试试这个:
if(! $rootScope.$root.$$phase) {
...
Handy little helper method to keep this process DRY: 方便的小助手方法来保持这个过程干燥:
function safeApply(scope, fn) {
(scope.$$phase || scope.$root.$$phase) ? fn() : scope.$apply(fn);
}
The digest cycle is a synchronous call. 摘要周期是同步调用。 It won't yield control to the browser's event loop until it is done. 在完成之前,它不会控制浏览器的事件循环。 There are a few ways to deal with this. 有几种方法可以解决这个问题。 The easiest way to deal with this is to use the built in $timeout, and a second way is if you are using underscore or lodash (and you should be), call the following: 解决这个问题最简单的方法是使用内置的$ timeout,第二种方法是使用下划线或lodash(你应该这样),请调用以下内容:
$timeout(function(){
//any code in here will automatically have an apply run afterwards
});
or if you have lodash: 或者如果你有lodash:
_.defer(function(){$scope.$apply();});
We tried several workarounds, and we hated injecting $rootScope into all of our controllers, directives, and even some factories. 我们尝试了几种解决方法,我们讨厌将$ rootScope注入我们的所有控制器,指令甚至一些工厂。 So, the $timeout and _.defer have been our favorite so far. 所以,到目前为止,$ timeout和_.defer一直是我们的最爱。 These methods successfully tell angular to wait until the next animation loop, which will guarantee that the current scope.$apply is over. 这些方法成功地告诉angular等待下一个动画循环,这将保证当前范围。$ apply结束。