angularjssettimeoutangularjs-timeout

AngularJS - Change Tracking


I'm new to AngularJS. I'm looking at using the $timeout service. I've read that one of the reasons for using $timeout over the JavaScript setTimeout function is because changes to variables in the $scope don't get updated in the UI until $scope.$apply is called. While that's the word, I'm unable to confirm this with actual code. I've written the following:

index.html

<div ng-app="myApp">
  <div ng-controller="myController">
    <div>AngularJS Count: {{total1}}</div>
    <br />

    <div>setTimeout Count: {{total2}}</div>
    <br />

    <button ng-click="start()">start</button>
  </div>
</div>

myController.js

var app = angular.module("myApp", []);
function myController($scope, $timeout) {
  $scope.total1 = 0;
  $scope.total2 = 0;

  $scope.isStarted = false;
  $scope.toggle = function() {
    if (!$scope.isStarted) {
      $scope.isStarted = true;

      $timeout(ngUpdate, 1000);      
      setTimeout(jsUpdate, 1000);
    }    
  };  

  function ngUpdate() {
      $scope.total1 = $scope.total1 + 1;
  }

  function jsUpdate() {
      $scope.total2 = $scope.total2 + 1;
  }
}

In this code sample, the changes to the variables on the $scope are updated in the UI. I'm trying to see a scenario, in code, where a change via the setTimeout function doesn't update the UI until $scope.$apply is called. Am I misunderstanding? Or was a change made to the AngularJS framework that makes the original assertion out-of-date.


Solution

  • Since, you are calling setTimeout from inside $scope.toggle, this code is already executing in the context of a $digest cycle. So, there is no need to call $apply.

    $timeout will test if it is in a $digest and then only call $apply if it needs to. This is very useful when building directives that bind to events that are happening outside of the angular context.

    Here is a directive scenario when $timeout is needed:

    app.directive('myDirective', function() {
    
        return {
            link: function(scope, element) {
    
                element.bind('click', function() {
    
                    // $timeout is needed here because the 'click' event is outside angular
                }
            }
        }
    });