Updating $scope Data in Asynchronous Functions


One of the many lovely things about Angular is the fluid two-way data binding that we get with the framework. Although not entirely perfect when it comes down to performance, it saves us quite a lot of time and effort when writing web applications. When it comes to asynchronousy, however, it should be noted that doing changes to your $scope data does not propagate to the view implicitly (!). In other words, if you do a change to a $scope variable from asynchronous context, the change is not reflected in the view. Let’s look at an example. Consider the following controller that uses JavaScript’s asynchronous setInterval function to update a counter every second:

function Ctrl ($scope) {
    $scope.counter = 0;
    
    setInterval(function() {
        $scope.counter++;
    }, 1000);
}

This code looks pretty good and dandy, right? Unfortunately, no. Although the counter does get updated every second, it does not propagate the change to the view. There are a couple of ways to solve this issue. Invoking $apply Manually The simplest and most straightforward way is to invoke $apply manually in the asynchronous context. Consider the following change to our initial example (lines 5 - 7):

function Ctrl ($scope) {
    $scope.counter = 0;
    
    setInterval(function() {
        $scope.$apply(function () {
            $scope.counter++;
        });
    }, 1000);
}

This change will force the counter updates through to the view. Using Angular’s Asynchronous Services This approach is case specific. For instance, you can use the services $interval or $timeout, which actually behind the scenes invoke $apply - relieving you from doing so manually. Considering our initial example, we can therefore inject and use $interval in our controller instead:

function Ctrl ($scope, $interval) {
    $scope.counter = 0;

    $interval(function () {
        $scope.counter++;
    }, 1000);
}

As the previous approach, this will propagate the counter updates to the view. I recommend using Angular’s services wherever this is possible and seems appropriate to the case at hand. However, always keep in mind that such services invoke $apply for you behind the scenes.

Hope you enjoyed this Sunday's little reading! :)