Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: watch not working inside loop

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

like image 707
user1131522 Avatar asked Jan 09 '23 14:01

user1131522


1 Answers

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());
              }
          }
      );
  }
like image 107
Gent Avatar answered Jan 17 '23 12:01

Gent