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
This will be solve your problem:
if(!$scope.$$phase) {
//TODO
}
I had the same problem with third parties scripts like CodeMirror for example and Krpano, and even using safeApply methods mentioned here haven't solved the error for me.
But what do has solved it is using $timeout service (don't forget to inject it first).
Thus, something like:
$timeout(function() {
// run my code safely here
})
and if inside your code you are using
this
perhaps because it's inside a factory directive's controller or just need some kind of binding, then you would do something like:
.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();
}]
)
use $scope.$$phase || $scope.$apply();
instead
First of all, don’t fix it this way
if ( ! $scope.$$phase) {
$scope.$apply();
}
It does not make sense because $phase is just a boolean flag for the $digest cycle, so your $apply() sometimes won’t run. And remember it’s a bad practice.
Instead, use $timeout
$timeout(function(){
// Any code in here will automatically have an $scope.apply() run afterwards
$scope.myvar = newValue;
// And it just works!
});
If you are using underscore or lodash, you can use defer():
_.defer(function(){
$scope.$apply();
});
See http://docs.angularjs.org/error/$rootScope:inprog
The problem arises when you have a call to $apply
that is sometimes run asynchronously outside of Angular code (when $apply should be used) and sometimes synchronously inside Angular code (which causes the $digest already in progress
error).
This may happen, for example, when you have a library that asynchronously fetches items from a server and caches them. The first time an item is requested, it will be retrieved asynchronously so as not to block code execution. The second time, however, the item is already in cache so it can be retrieved synchronously.
The way to prevent this error is to ensure that the code that calls $apply
is run asynchronously. This can be done by running your code inside a call to $timeout
with the delay set to 0
(which is the default). However, calling your code inside $timeout
removes the necessity to call $apply
, because $timeout will trigger another $digest
cycle on its own, which will, in turn, do all the necessary updating, etc.
Solution
In short, instead of doing this:
... your controller code...
$http.get('some/url', function(data){
$scope.$apply(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
do this:
... your controller code...
$http.get('some/url', function(data){
$timeout(function(){
$scope.mydate = data.mydata;
});
});
... more of your controller code...
Only call $apply
when you know the code running it will always be run outside of Angular code (e.g. your call to $apply will happen inside a callback that is called by code outside of your Angular code).
Unless someone is aware of some impactful disadvantage to using $timeout
over $apply
, I don't see why you couldn't always use $timeout
(with zero delay) instead of $apply
, as it will do approximately the same thing.
Understanding that the Angular documents call checking the $$phase
an anti-pattern, I tried to get $timeout
and _.defer
to work.
The timeout and deferred methods create a flash of unparsed {{myVar}}
content in the dom like a FOUT. For me this was not acceptable. It leaves me without much to be told dogmatically that something is a hack, and not have a suitable alternative.
The only thing that works every time is:
if(scope.$$phase !== '$digest'){ scope.$digest() }
.
I don't understand the danger of this method, or why it's described as a hack by people in the comments and the angular team. The command seems precise and easy to read:
"Do the digest unless one is already happening"
In CoffeeScript it's even prettier:
scope.$digest() unless scope.$$phase is '$digest'
What's the issue with this? Is there an alternative that won't create a FOUT? $safeApply looks fine but uses the $$phase
inspection method, too.