ElRudi
ElRudi

Reputation: 2324

angularjs directive watch not fired on object function property

TL;DR: updating a property on a watched object does not seem to fire the watch when the property is a function .

Live example

See this Plnkr exampl.

The config object's properties can be changed by clicking the buttons in the example. The objects are in sync (between directive and controller), and the $watch function in the directive (which simply ups a counter in this case) is called when a property is changed. Except when a function property is changed, which is what I do not understand.

Dead example

View

<main-dir config="configObject"></main-dir> 

Directive

myApp.directive('mainDir', function () {

    return {
        restrict: 'E',
        scope: {config: '='},
        link: function (scope, el) {

            //(initialisation code)
                           
            scope.$watch("config", function (config) {
                if (!config) return;
                // do stuff with config object
            }, true);

         }
     };
 });   

Controller

//init:
$scope.configObject = {
    propA: "value",
    propB: [1,2,3],
    propC: {key0: 3, key1: "value"},
    propD: function(x) {return x+1;}
}

//elsewhere:
$scope.configObject.propA = "other value"; //fires watch in directive
//or
$scope.configObject.propB = [3,2,1]; //fires watch in directive
//or
$scope.configObject.propB.push(5); //fires watch in directive
//or
$scope.configObject.propC = {key0: 1, key2: 34}; //fires watch in directive
//or
$scope.configObject.propC["key100"] = "newValue"; //fires watch in directive
//
// ... BUT ...
//
$scope.configObject.propD = function (x) {return x+2;} //does NOT fire watch in directive

So, in short: I can get the $watch to fire on changes to the object's properties. But that doesn't seem to work if the property is a function.

Workaround:
When changing a property that is a function, I add a line changing a non-function property to fire the watch, like so:

$scope.configObject.propD = function (x) {/*...new function def...*/};
$scope.configObject.xxx = Math.random(); //<-- causes watch to fire

Does anyone know why that is the case? Can I get the $watch to fire also on changes to function properties of the config object?

Thanks!

Upvotes: 0

Views: 1193

Answers (1)

Artur Kupiec
Artur Kupiec

Reputation: 61

scope.$watch("config.propD") will watch reference to function instead of returning value scope.$watch("config.xxx") will watch return value from Math.random because you called function

Simplest way is to use $watchCollection documentation but it's not perfect one

  scope.$watchCollection('config',
    function(config) {
      scope.caught++;
    }
  );

Another way is to program collection of $watch'ers it's work slightly better:

  angular.forEach(scope.config, function(value, key) {
    scope.$watch(function() {
      return scope.config[key];
    }, function(config) {
      scope.caught2++;
    }, true);
  });

Upvotes: 1

Related Questions