4

In angular if I register watch events dynamically (in my case in a for loop), the watch does not work. Please take a look at a fiddle. Any ideas?

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


function MyCtrl($scope, $parse) {

    // 1. binding watch inside loop (doesn't work):
    $scope.aaa = 1;
    $scope.bbb = 2;

    $scope.dependsOn = [
        function () { return $scope.aaa; },  
        function () { return $scope.bbb; }
    ];        

    for (var i = 0; i < $scope.dependsOn.length; i++) {        

        $scope.$watch(
            function () {
                return $scope.dependsOn[i];                    
            },
            function (newVal) {
                if (newVal !== undefined) {
                    console.log("doesn't work");
                }
            }
        );
    }    

    $scope.aaa = 5;
    $scope.bbb = 6;    

    // binding watch not inside loop (works):

    $scope.ccc = 1;
    $scope.ddd = 2;    

    $scope.dependsOn = [
        function () { return $scope.ccc; },
        function () { return $scope.ddd; }
    ];    

    $scope.$watch(
        function () {
            return $scope.dependsOn[0];                    
        },
        function (newVal) {
            if (newVal !== undefined) {
                console.log("works");
            }
        }
    );    

    $scope.$watch(
        function () {
            return $scope.dependsOn[1];                    
        },
        function (newVal) {
            if (newVal !== undefined) {
                console.log("works");
            }
        }
    );     

    $scope.ccc = 5;
    $scope.ddd = 6;      
}

fiddle

1 Answer 1

9

The problem you are experiencing is because you are capturing the variable i in a closure. Then i gets incremented to the value 2 and drops out of the loop, each of your delegates will have 2 as their i value upon actually executing the delegate.

Demonstrates: http://jsfiddle.net/df6L0v8f/1/ (Adds:)

$scope.$watch(
     function () {
         console.log(i);
         return $scope.dependsOn[i];                    
     },
    function (newVal) {
         if (newVal !== undefined) {
              console.log("doesn't work");
         }
    }
);

You can fix this and the issue of variable hoisting by using a self calling closure to capture the value at the time of iteration and maintain that for your delegate.

http://jsfiddle.net/df6L0v8f/4/

for (var i = 0; i < $scope.dependsOn.length; i++) {        
     var watchDelegate = (function(itemDelegate){
          return function () {
              return itemDelegate;                    
          };
      })($scope.dependsOn[i]);

      $scope.$watch(
          watchDelegate,
          function (newVal) {
              if (newVal !== undefined) {
                  console.log(newVal());
              }
          }
      );
  }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.