Error:
$digest
already in progress when calling$scope.$apply()
Raise error when calling $scope.$apply()
$scope.$apply(function () { |
Problem is fixed by calling $timeout
instead.
$timeout(function () { |
Comments:
Simply using $timeout
is not the best nor the right solution. Also, make sure that if you are concerned by performances or scalability.
Things we should know
$$phase
is private to the framework and there are good reasons for that.
$timeout(callback)
will wait until the current digest cycle (if any) is done, then execute the callback, then run at the end a full $apply
.
$timeout(callback, delay, false)
will do the same (with an optional delay before executing the callback), but will not fire an $apply
(third argument) which saves performances if you didn’t modify your Angular model ($scope
).
$scope.$apply(callback)
invokes, among other things, $rootScope.$digest
, which means it will redigest the root scope of the application and all of its children, even if you’re within an isolated scope.
$scope.$digest()
will simply sync its model to the view, but will not digest its parents scope, which can save a lot of performances when working on an isolated part of your HTML with an isolated scope (from a directive mostly). $digest
does not take a callback: you execute the code, then digest.
$scope.$evalAsync(callback)
has been introduced with angularjs 1.2, and will probably solve most of your troubles. Please refer to the last paragraph to learn more about it.
if you get the $digest
already in progress error, then your architecture is wrong: either you don’t need to redigest your scope, or you should not be in charge of that (see below).
How to structure your code
When you get that error, you’re trying to digest your scope while it’s already in progress: since you don’t know the state of your scope at that point, you’re not in charge of dealing with its digestion.
function editModel() { |
And if you know what you’re doing and working on an isolated small directive while part of a big Angular application, you could prefer $digest
instead over $apply
to save performances.
Update since Angularjs 1.2
A new, powerful method has been added to any $scope: $evalAsync
. Basically, it will execute its callback within the current digest cycle if one is occurring, otherwise a new digest cycle will start executing the callback.
That is still not as good as a $scope.$digest
if you really know that you only need to synchronize an isolated part of your HTML (since a new $apply
will be triggered if none is in progress), but this is the best solution when you are executing a function which you cannot know it if will be executed synchronously or not, for instance after fetching a resource potentially cached: sometimes this will require an async call to a server, otherwise the resource will be locally fetched synchronously.
In these cases and all the others where you had a ! $scope.$$phase
, be sure to use $scope.$evalAsync(callback)
.
Ref