I am a little confused about how $scope.$apply and digest loops function. From what I understand, since digest loops runs at regular intervals and not always, we can force the digest loop to run on certain scope variables which we want to update immediately. Also in the description here, it's given that $scope.$apply should be used when an async call is made, so that variables can be updated. My doubt is if digest loop doesn't run always, how are scope variables almost instantaneously updated in the view/controller?
Simply, use $scope.$apply() whenever you're outside the angular scope. For example within setTimeout function, as it is outside the world of angular.
Related
As i was going through the life cycle of scope, I came across $digest should be called by $apply. But I want to know if possible can we enable $digest without $apply. If yes what is disadvantage
when you calls the $scope.$apply() function, it call the $rootScope.$digest(). So as a result of that, digest cycle starts from the rootScope and call all the child scopes.
you can call the digest using $scope.$digest() but this will start the cycle for child scope only. sometimes binding will not properly occur because digest cycle is not starting from the root scope.
you can check this article to get an idea about how the digest cycle works
The $scope.$digest() function iterates through all the watches in the $scope object, and its child $scope objects (if it has any). When $digest() iterates over the watches, it calls the value function for each watch.
The $scope.$apply() function takes a function as parameter which is executed, and after that $scope.$digest() is called internally.
I have tried all three terms for refreshing the scope after triggering any event. Sometimes I got an error
$apply or $digest is already in progress.
Finally I am using $timeout without any error. But I don't know that which one is best as per the complexity or performance.
$timeout calls $apply which calls $digest.
In more detail:
$apply triggers a $digest cycle on root scope (therefore on all scopes within the application).
$digest can be called directly on a particular child scope instead of on root -- but watchers on parent or sibling scopes won't be triggered, so incautious use of $digest can leave different scopes visibly out of synch.
$timeout waits for the next digest cycle, and then calls $apply (and thus is basically a simple way to call $apply without worrying about "digest already in progress" errors.) The newer $evalAsync is similar but applies to the current digest if possible, so is sometimes slightly more performant than $timeout. (There is also $applyAsync which is as far as I can tell basically $timeout with a 10ms delay, for reasons which I'm sure make sense to someone.)
TL;DR: leave Angular to manage its own data lifecycle as much as possible. When you need to, use $evalAsync (or, honestly, $timeout is close enough; the difference is not that significant).
If you start running into performance issues, you can start looking for cases where you can safely switch to using $digest on specific scopes. (Personally I've not seen many cases where this is worth the added code complexity, but maybe that's just me.)
I just want to know how to use $digest. Inside a controller the following code works fine and it updates the DOM after 3 seconds:
setTimeout(function(){
$scope.$apply(function(){
$scope.name = 'Alice';
});
}, 3000);
However by using
setTimeout(function(){
$scope.$digest(function(){
$scope.name = 'Alice';
});
}, 3000);
nothing happens...
I thought that they do the same thing. Am I wrong?
$apply() and $digest() have some similarities and differences. They are similar in that they both check what's changed and update the UI and fire any watchers.
One difference between the two is how they are called. $digest() gets called without any arguments. $apply() takes a function that it will execute before doing any updates.
The other difference is what they affect. $digest() will update the current scope and any child scopes. $apply() will update every scope. So most of the time $digest() will be what you want and more efficient.
The final difference which explains why $apply() takes a function is how they handle exceptions in watchers. $apply() will pass the exceptions to $exceptionHandler (uses try-catch block internally), while $digest() will require you handle the exceptions yourself.
I think you must go through documents $apply
$apply() is used to execute an expression in angular from outside of
the angular framework
Usually, you don't call $digest() directly in controllers or in
directives. Instead, you should call $apply() (typically from within a
directive), which will force a $digest().
Also as suggest by Jorg, use $timeout
In angularjs $scope object is having different functions like $watch(), $digest() and $apply() and we will call these functions as central functions.
The angularjs central functions $watch(), $digest() and $apply are used to bind data to variables in view and observe changes happening in variables.
Generally in angularjs we use $scope object to bind data to variables and use that variables values wherever we required in application. In angularjs whatever the variables we assigned with $scope object will be added to watch list by using $scope.$watch() function.
In angularjs once variables added to watch list $scope.digest() function will iterates through the watch list variables and check if any changes made for that variables or not. In case if any changes found for that watch list variables immediately corresponding event listener function will call and update respective variable values with new value in view of application.
The $scope.$apply() function in angularjs is used whenever we want to integrate any other code with angularjs. Generally the $scope.$apply() function will execute custom code and then it will call $scope.$digest() function forcefully to check all watch list variables and update variable values in view if any changes found for watch list variables.
In most of the time angularjs will use $scope.watch() and $scope.digest() functions to check and update values based on the changes in variable values.
A better understanding of $watch, $apply, and $digest is provided in this article.
Simply,
$watch that is used to detect changes in the UI
$apply would tell the $digest loop what changes should be applied
$digest loop would run and will ask every $watch for changes, the DOMs would change accordingly for what has what has been applied
You should call $apply only.
I'm trying to better understand the nuances of using the $timeout service in Angular as a sort of "safe $apply" method. Basically in scenarios where a piece of code could run in response to either an Angular event or a non-angular event such as jQuery or some standard DOM event.
As I understand things:
Wrapping code in $scope.$apply works fine for scenarios where you
aren't already in a digest loop (aka. jQuery event) but will raise an error if a digest is in progress
Wrapping code in a $timeout() call with no delay parameter works whether already in a digest cycle or not
Looking at Angular source code, it looks like $timeout makes a call to $rootScope.$apply().
Why doesn't $timeout() also raise an error if a digest cycle is already in progress?
Is the best practice to use $scope.$apply() when you know for sure that a digest won't already be in progress and $timeout() when needing it to be safe either way?
Is $timeout() really an acceptable "safe apply", or are there gotchas?
Thanks for any insight.
Looking at Angular source code, it looks like $timeout makes a call to
$rootScope.$apply().
Why doesn't $timeout() also raise an error if a digest cycle is already in progress?
$timeout makes use of an undocumented Angular service $browser. Specifically it uses $browser.defer() that defers execution of your function asynchronously via window.setTimeout(fn, delay), which will always run outside of Angular life-cycle. Only once window.setTimeout has fired your function will $timeout call $rootScope.$apply().
Is the best practice to use $scope.$apply() when you know for sure that a digest won't already be in progress and $timeout() when needing it to be safe either way?
I would say so. Another use case is that sometimes you need to access a $scope variable that you know will only be initialized after digest. Simple example would be if you want to set a form's state to dirty inside your controller constructor (for whatever reason). Without $timeout the FormController has not been initialized and published onto $scope, so wrapping $scope.yourform.setDirty() inside $timeout ensures that FormController has been initialized. Sure you can do all this with a directive without $timeout, just giving another use case example.
Is $timeout() really an acceptable "safe apply", or are there gotchas?
It should always be safe, but your go to method should always aim for $apply() in my opinion. The current Angular app I'm working on is fairly large and we've only had to rely on $timeout once instead of $apply().
If we use $apply heavily in the application, we might get the Error: $digest already in progress. It happens because one $digest cycle can be run at a time. We can resolve it by $timeout or by $evalAsync.
The $timeout does not generate error like "$digest already in progress“ because $timeout tells Angular that after the current cycle, there is a timeout waiting and this way it ensures that there will not any collisions between digest cycles and thus output of $timeout will execute on a new $digest cycle.
I tried to explain them at : Comparison of apply, timeout,digest and evalAsync.
May be it will help you.
As far as I understand it, $timeout is a wrapper around setTimeout which implicitly calls $scope.$apply, meaning it runs outside of the angular lifecycle, but kickstarts the angular lifecycle itself. The only "gotcha" I can think of is that if you're expecting your result to be available this $digest, you need to find another way to "safe apply" (which, AFAIK, is only available via $scope.$$phase).
Demo here
Quick question: in the following code I only call the function isSpecificPage() once, why it console.log twice?
<div ng-hide="isSpecificPage()">
<p>Hello!</p>
</div>
Angular puts a watch on your ng-hide function so that every digest cycle it can see if the results changed (and thus if it needs to change from hiding to showing your element or vice-versa).
When watched functions are evaluated (during $digest) if any of them have changed from the previous $digest then Angular knows that change might ripple through to other watched functions (perhaps the changed variable is used in another watched function). So every watch is re-evaluated (also called dirty processing) until none of the watches results in a change. Thus typically you'll see 2 calls to watched functions per digest and sometimes more (up to 10- at 10 loops through it gives up and reports an error saying it can't stabilize).
Here's more on watch and digest:
http://docs.angularjs.org/api/ng.$rootScope.Scope
http://www.benlesh.com/2013/08/angularjs-watch-digest-and-apply-oh-my.html
ng-hide is one of the directives that uses $watch internally. Since $watch uses digest cycle(which runs atleast 2 times to check if the value has changed or not), so your function isSpecificPage has run twice.
For a list of directives that use $watch internally, refer this stackoverflow answer directives that add watch internally.