$scope.$apply() When to use it ??
Alan Buscaglia
Google Developer Expert Angular | Microsoft MVP | Front End Architect | Gentleman Programming Community Owner | “Como ser front-end sin fallar en el intento” Book Author
Hello developers, let's talk about something you use everyday but......do you know why you use it ?, or when ?.
Javascript is a turn based language, what does that mean ?, that the code executes in turns, while it runs nothing can interrupt it, and nothing else goes in the browser. Thats why when we have an Ajax call for example, that takes time, we set a callback function so we finish the turn and create a new one when the event we are waiting happens, and the callback runs to it's end.
for example:
function letsWaitALittle() {
alert("i've waited enough, i'm calling this one");
}
setTimeout(letsWaitALittle, 5000);
First JavaScript runs a single turn to load the code, he encounters a setTimeout function so he sets a timeout. When the code is fully loaded if needed the browser is going to update de DOM, if an error happens the screen remains white (don't do like it didn't happened to you).
When the 5000 milliseconds are gone, a new turn is created for the letsWaitALittle function we have created.
So now that we know all this.....how is that AngularJS do the binding we all love ?. Well first and all, when you bind an element of the view to a variable on the $scope object, AngularJS creates an internal watch, so he can see any changes in the variable, the function we are talking about is $scope.watch().
While our application is running, AngularJS calls the $scope.$digest() function, this one goes through all the watches and checks if any of the variables being watched have changed, if one of them have, the correct listener function is called. The listener function executes the code defined inside it, for example, if our variable is a string that has been bound to a span, and it changes, the watch is going to call a listener function, this one will change the content of the span with the new string inside our variable. So we can easily say that the $digest() function is the one in charge of triggering the update.
So, if $digest() is the one doing the update, what is the $apply() function that we use ?. This function takes a function or an AngularJS expression string, and then, it calls the $digest() function so all the watchers we have are checked. When do we need to use it then ?, because if you think about it, when we change a variable AngularJS automatically takes control of what has to be done, for example, ng-click, ng-class, etc. are events that are all wrapped in $scope.$apply().
As we talked before, JavaScript is a turn based language, so what happens when AngularJS doesn't know we are creating a new turn ?, well my friends THEN is when we use $apply(), for example, if we use setTimeout() we will create a new turn that calls a function when the time is over, AngularJS doesn't know about this new turn so anything we did in the function is not going to be reflected in the DOM, and there my friends is when we use $scope.$apply() .
Example:
setTimeout(function (){
$scope.$apply(function (){
$scope.domtext = "i've waited enough, i'm calling this one";
});
},5000);
other way is :
setTimeout(function (){
$scope.domtext = "i've waited enough, i'm calling this one";
$scope.$apply();
},5000);
This two are going to achieve the same results, but for example, let's say that we are a little clumsy with our fingers and we make a syntax error, if the code is not inside the $apply() function, like the second example, the error is going to be thrown outside AngularJS, and if you have any error handling in you app, well....it's not going to work. $apply() by default has a try/catch inside, so the error is always caught and the $digest() is going to continue, this continuing of the $digest() function is made thanks a "finally" clause.
Ok! i hope you liked this short explanation, and learned how to use this great feature, Good Bye everyone!