javascriptangularjsangularjs-scopeangularjs-watch

Function scope messes with AngularJS Watches


I'm currently in a situation where I need to create a few watches based on the properties of an object.

These properties are used to group functions that depend on the same variable / expression.

While creating the $watch functions in the loop, it seems to well, but when the watches actually execute, only the last property is persisted in the function scope. Which means that for all $watches (which evaluate different expressions), the functions that get executed are the same.

for (var prop in obj) {
    if (obj.hasOwnProperty(prop) {
        $scope.$watch(function() { 
            return evaluateExpression(obj[prop].expression);
        }, function(newVal, oldVal) {
            evaluateDependentExpressions(obj[prop].dependentExpressions);
        }); 
    }
}

Now if my obj variable looks like this:

var obj = {
    'Person.Name': {
        expression: ...,
        dependentExpressions: [...]
    },
    'Person.Age': {
        expression: ...,
        dependentExpressions: [...]
    }
};

Then the function evaluateDependentExpressions is called twice in where the value of prop = 'Person.Age'.

Any help how to solve the function scope problem is greatly appreciated.

plunk


Solution

  • This is known problem in JavaScript prop variable is set to last used value in for (var prop in obj), simple workaround is to use IIFE:

    for (var p in obj) {
      (function(prop) {
         // your code
    
         if (obj.hasOwnProperty(prop) {
           $scope.$watch(function() { 
            return evaluateExpression(obj[prop].expression);
           }, function(newVal, oldVal) {
            evaluateDependentExpressions(obj[prop].dependentExpressions);
           }); 
         }
       })(p);
    }
    

    Explanation here: JavaScript closure inside loops – simple practical example