Search code examples
javascripthtmlangularjsangularjs-scope

AngularJS: scope watch not being triggered, even with objectEquality = true


In my controller I have:

$scope.$watch('numRuns', function(newVal, oldVal) {
  if (newVal === oldVal) return;
  updateNumResults();
});

function updateNumResults() {
  $scope.resultSet.size($scope.numRuns);
  console.log("updateNumResults called");
}

The size method in ResultSet looks like:

  function size(n) {
    if (n === undefined) return numResults_; // no arg => using as getter
    numResults_ = n;
    return this;
  }

In my view I have:

Number of runs: <input ng-model="numRuns"/>

In my directive I have:

scope.$watch('resultSet', function(newVal, oldVal) {
  console.log("change in result set");
  createFromScratch();
}, true); // use object equality

If I type into the input box in the browser, I'll see the output "updateNumResults called", but the actual watch on resultSet is not called. I have verified that the object resultSet is indeed getting mutated by the call to size(), and since I have setup the watch with the objectEquality parameter set to true, my understanding was that the change should be getting picked up and triggering the watch. Why is that not happening and how can I make it happen?

UPDATE

I made the following changes to make it work:

Created a simple counter variable that I manually change each time I call any mutator method on resultSet:

  function updateNumResults() {
    $scope.resultSet.size($scope.numRuns);
    $scope.resultSetChanged++; 
    console.log("updateNumResults called");
  }

and I watch that instead in the directive:

  function watchResultSet() {
    scope.$watch('resultSetChanged', function(newVal, oldVal) {
      console.log("change in result set");
      createFromScratch();
    });
  }

This solution works fine, and is probably less resource intensive, but I would still like to know why the original solution is not working.

UPDATE 2 WITH JSBIN EXAMPLES

Sh0ber's idea was correct. The problem was with private vars.

  1. JSbin using private vars (revealing module pattern) showing the problem
  2. JSbin using normal objects works

Solution

  • This answer is based on some discussion in the question:

    This code will work if the resultSet object is mutated. After looking at the size method it appeared to only be altering some private vars in resultSet's constructor. The resultSet object itself was not changing and so the $watch was never triggered.