17

I'm attempting to set up a watch in AngularJS and I'm clearly doing something wrong, but I can't quite figure it out. The watch is firing on the immediate page load, but when I change the watched value it's not firing. For the record, I've also set up the watch on an anonymous function to return the watched variable, but I have the exact same results.

I've rigged up a minimal example below, doing everything in the controller. If it makes a difference, my actual code is hooked up in directives, but both are failing in the same way. I feel like there's got to be something basic I'm missing, but I just don't see it.

HTML:

<div ng-app="testApp">
    <div ng-controller="testCtrl">
    </div>
</div>

JS:

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

function testCtrl($scope) {
    $scope.hello = 0;

    var t = setTimeout( function() { 
        $scope.hello++;
        console.log($scope.hello); 
    }, 5000);

    $scope.$watch('hello', function() { console.log('watch!'); });
}

The timeout works, hello increments, but the watch doesn't fire.

Demo at http://jsfiddle.net/pvYSu/

John Slegers
  • 45,213
  • 22
  • 199
  • 169
David Kiger
  • 1,958
  • 1
  • 16
  • 25

3 Answers3

40

It's because you update the value without Angular knowing.

You should use the $timeout service instead of setTimeout, and you won't need to worry about that problem.

function testCtrl($scope, $timeout) {
    $scope.hello = 0;

    var t = $timeout( function() { 
        $scope.hello++;
        console.log($scope.hello); 
    }, 5000);

    $scope.$watch('hello', function() { console.log('watch!'); });
}

Or you could call $scope.$apply(); to force angular to recheck the values and call watches if necessary.

var t = setTimeout( function() { 
    $scope.hello++;
    console.log($scope.hello); 
    $scope.$apply();
}, 5000);
Martin
  • 8,876
  • 2
  • 35
  • 36
  • 7
    The use of `setTimeout` was just for the example. `.$apply()` was what I was missing in the real world. Thanks! – David Kiger Mar 27 '13 at 17:12
1

You can use without $interval and $timeout

$scope.$watch(function() {
            return variableToWatch;
        }, function(newVal, oldVal) {
            if (newVal !== oldVal) {
                //custom logic goes here......
            }
        }, true);
sumit_suthar
  • 412
  • 3
  • 15
0

It can also happen because the div is not registered with the controller. Add a controller to your div as follows and your watch should work:

<div ng-controller="myController">
Rakesh
  • 4,004
  • 2
  • 19
  • 31