Angular.js

Angular.js Scope Apply Example

If you’re even a little bit into learning Angular and have researched the main points and have the basic understanding of it, you will have noticed that the first thing everyone should have a good grasp on, is the two-way data binding that AngularJS offers. But what is data binding and how can you implement it in your Angular apps? how simple this might be? Let’s find out!

1. Two-way data binding

We will have to start this section by explaining what is two-way data binding and how does it work, so that we can get to a proper explanation of $apply. Two-way data binding means that the $scope and the view are connected to each other in a way that makes $scope changes whenever the view changes and vise versa that means the view will change whenever you change the $scope.

By now you will have come across quite a bit of vocabulary when searching for data binding, such as $apply, $digest, $watch, dirty-checking etc. But how exactly does Angular use all that in order to implement two-way data binding?

1.1 Turn-based Javascript

You know theoretically that poorly-written Javascript code can freeze a Webpage, right? But why exactly does that happen? It happens because Javascript is turn-based, which means that not all the code is executed at once, but it’s divided into parts which take turns to execute. One goes first, then the other, and so on until all the code is executed. While one turn is being executed the browser does not do anything else. It concentrates all it’s power on the task at hand, while temporarily freezing the browser. These freezing sessions are made visible when the code is poorly written because you might unwittingly code never-ending loops, and similar stuff that would freeze webpages.

1.2 How do we update views?

When reading up on AngularJS, you might have noticed that it is very often referred to, as having an MVC architecture. Now let’s explain shortly what MVC is: It stands for Model View Controller. The model is the element that is responsible for managing application data. It responds to the request from view and to the instructions from controller to update itself. The view is a nice looking representation of data in a particular format, triggered by the controller’s decision to present the data. So Angular let’s us bind the data from the visual part of the application or the interface to the code behind it, which would be what we make the app do from behind the scenes. But how does AngularJS know when the data changes making the view need to be updated?

The code will definitely need to know when the value of a variable has changed in order for the app to work as we would want it to. However, there is no direct way to let it know about the changes immediately. Instead, there are two widely used strategies in the MVC world of programming.

The first strategy would be what I would call the verbose and faulty one, which is obviously not used by Angular since Angular is kind of the best out there. This strategy consists of the usage of special objects, where data is set via methods, not property assignments. The changes can then be noted, and the page can be updated. It’s widely used by EmberJS and KnockoutJS because it is simple to use for the programmer. However, it’s downsides lie on the app’s efficiency. It’s already pretty obvious that you would have to create a special object just for that purpose. The other annoying trait it has, is that you will have to use the long way to assign a value to an object key. That means that you will have to assign values like this:

syntax.js

obj.set('key', 'value') 

instead of this:

syntax.js

obj.key = 'value'

The second strategy, which is also the one that Angular uses, is that after each turn of the execution of the Javascript code, you see if the values of the variables have changed. Doing a check each execution turn seems a lot more inefficient than the first strategy at first, don’t you think? You don’t have to worry though, as there is a multitude of methods to make this strategy a very efficient one. The big benefit is that we can use normal objects and update our data however we want, and the changes will be noticed and reflected in our bindings. For this strategy to work, we need to know when data have possibly changed, and this is where $scope.$apply comes into play.

2. $apply vs. $digest

$apply() and $digest() are two core, and sometimes confusing, aspects of AngularJS. To understand how AngularJS works one needs to fully understand how both $apply() and $digest() work, as they are pretty much inseparable from each other. We said that AngularJS uses two-way data binding in order to get the view to change according to updates made to the $scope model and vice versa. What’s the magic trick behind that?

2.1 The watcher

Whenever you write an Angular expression in your code, such as {{aModel}}, Angular sets up a general watcher on the $scope model that monitors it’s changes and does the updating when it does change. This watcher is just like any other Angular watcher, generally looking like below:

syntax.js

$scope.$watch('aModel', function(newValue, oldValue) {
  //update the DOM with newValue
});

As you see, all you have to do is fire the $watch() method on the $scope model, while giving it two parameters as arguments. The first one matches the name of the variable while the second is what is commonly called a listener. It’s just a function, evoked when the aModel changes it’s value from the oldValue to the newValue. But… how does the watcher know when the model has changed? It’s the $digest cycle’s turn!

2.2 The $digest cycle

The $digest cycle is pretty much a loop in which AngularJS checks for any changes to its bindings plus anything else that is being watched and lives inside its $scope. However, not everything that lives inside the $scope is being watched by Angular, only variables used in expressions such as {{aModel}} and also when $scope.$watch is being used.

Once a watch is fired Angular starts a new $digest cycle in which it checks all current variables and compares them with their previous values. If it finds any change it will then update the correspondent bindings, if it doesn’t then it will wait until the next watch fires.

2.3 The $apply

Even though it’s the $digest where the magic will happen, we almost never call it directly, instead we use $scope.$apply() which will call $scope.$digest() for us. $scope.$apply() will take a function or an Angular expression string, and execute it. After that it will call $scope.$digest() to update any bindings or watchers. How often will you need to run $scope.$apply()? Very rarely!

Whenever the browser receives an event that can be managed by the angular context the $digest loop will be fired and you will not need to run $scope.$apply() by yourself. Events like ng-click, controller initialization and $http callbacks are all wrapped in $scope.$apply(), so you don’t have to call it yourself. In fact, if you tried, you’d have an error thrown at you, since you are not allowed to call an $scope.$apply() inside of another one. So when are you required to call $scope.$apply() then?

3. When do you call $apply manually?

There are cases when if you don’t call $apply() manually, it won’t work at all and the two-way data binding will not work for you. Why does that happen? It’s because jQuery doesn’t call $apply and then the events never enter the angular context and then the $digest loop is never fired. They go step by step right after one another to create an avalanche in the end.

You do need to run $apply by yourself if you are going to run code in a new turn, and only if that turn isn’t being created from a method in the AngularJS library. This is the general rule to be followed regarding the manual firing of $apply. But let’s see an example to make it more clear.

4. $apply in the game!

Let’s take a general example that would better illustrate when we do need to use $apply manually and when not. Suppose we already have a value for the scope of one of our variables and want to update it after 5 seconds. The code for the HTML part of the mini app would go like below:

index.html

 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Try Apply</title>

    <script src="bower_components/angular/angular.min.js"></script>

    <link rel="stylesheet" href="bower_components/bootstrap/dist/css/bootstrap.min.css"/>
    <script src="bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
    <script src="bower_components/jquery/dist/jquery.min.js"></script>

    <!-- Custom files -->
    <script src="app.js"></script>
</head>

<body ng-app="app">

    <div ng-controller="Ctrl">
       <p>{{message}}</p>
    </div>

</body>
</html>

What we have done here is pretty simple. The code in the <head> tag is pretty much all linking and scripting of the external links in order to make our app functional. What’s important is the content of the <body> tag. We have put the ng-app="app" inside of this tag to show the browser that all the document is an Angular app. The rest is a simple paragraph containing an Angular expression that just displays the initial content of the message variable and after a delay of 2 seconds is supposed to display the changes made to the data. However, that would all be moot without the Javascript part of our code:

app.js

 
var app=angular.module('app',[]);

app.controller('Ctrl', function($scope) {
                             $scope.message = "Waiting 2000ms for update";

                                 setTimeout(function () {
                                         $scope.message = "Timeout called!";
                                 }, 2000);
                           });

Here we have simply created an Angular module named app and the Ctrl controller that after 2 seconds changes the value of $scope.message from Waiting 2000ms for update to Timeout called!. However, what we get 2 seconds after evoking this code is just the first message, and not the updated one. What should we do now?! The answer would be: Just a small modification in the code for the controller that should make it look like this:

app.js

 
var app=angular.module('app',[]);

app.controller('Ctrl', function($scope) {
                             $scope.message = "Waiting 2000ms for update";

                                 setTimeout(function () {
                                     $scope.$apply(function () {
                                         $scope.message = "Timeout called!";
                                     });
                                 }, 2000);
                           });

It’s almost the same, except that now we have wrapped the timeout function with $apply(). By doing this we will have placed the message variable inside the angular context which would make it able to be watched and updated according to the changes.

Sometimes  $scope.$apply() is called with no arguments. This achieves the desired result, but misses some opportunities regarding error handling. If you use $apply without a function passed to it as an argument, and that function throws an error, that error is thrown outside of the angular context and that means that any error handling would be unable to catch it. Comparatively, $apply not only runs your code, but runs it in a try/catch so your error is always caught, and the $digest call is in a finally clause, meaning it will run regardless of an error being thrown and that makes things insanely easy at times!

Now you’re fully able to understand and use $apply in AngularJS and never again have your two-way data binding be faulty and problematic!

5. Download the source code

This was an example of $apply in AngularJs.

Download
You can download the full source code of this example here: $apply

Era Balliu

Era is a Telecommunications Engineering student, with a great passion for new technologies. Up until now she has been coding with HTML/CSS, Bootstrap and other front-end coding languages and frameworks, and her recent love is Angular JS.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
rechelyn
rechelyn
7 years ago

Thank you so much!you gave me a very informative details about AngularJS since we have started to discuss about Javascript….God Bless you!

suresh
suresh
7 years ago

Thank youuuuuuuuuuuuuu………. Era .

Back to top button