首页 文章

如何深度观察angularjs中的数组?

提问于
浏览
309

我的范围中有一个对象数组,我想要观察每个对象的所有值 .

这是我的代码:

function TodoCtrl($scope) {
  $scope.columns = [
      { field:'title', displayName: 'TITLE'},
      { field: 'content', displayName: 'CONTENT' }
  ];
   $scope.$watch('columns', function(newVal) {
       alert('columns changed');
   });
}

但是当我修改这些值时,例如我将 TITLE 更改为 TITLE2alert('columns changed') 从未弹出 .

如何深入观察数组内的对象?

有一个现场演示:http://jsfiddle.net/SYx9b/

10 回答

  • 13

    您可以将 $watch 的第3个参数设置为 true

    $scope.$watch('data', function (newVal, oldVal) { /*...*/ }, true);
    

    https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch

    从Angular 1.1.x开始,您还可以使用$ watchCollection来观看浅表(只是“第一级”)的集合 .

    $scope.$watchCollection('data', function (newVal, oldVal) { /*...*/ });
    

    https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection

  • 1

    在你的$ watch中深度潜水物体会产生性能影响 . 有时(例如,当更改只是推送和弹出时),您可能希望$观察一个容易计算的值,例如array.length .

  • 4

    如果你只想看一个数组,你可以简单地使用这段代码:

    $scope.$watch('columns', function() {
      // some value in the array has changed 
    }, true); // watching properties
    

    example

    但这不适用于多个数组:

    $scope.$watch('columns + ANOTHER_ARRAY', function() {
      // will never be called when things change in columns or ANOTHER_ARRAY
    }, true);
    

    example

    为了处理这种情况,我通常将我想要观看的多个数组转换为JSON:

    $scope.$watch(function() { 
      return angular.toJson([$scope.columns, $scope.ANOTHER_ARRAY, ... ]); 
    },
    function() {
      // some value in some array has changed
    }
    

    example

    正如@jssebastian在评论中指出的那样, JSON.stringify 可能比 angular.toJson 更可取,因为它可以处理以'$'开头的成员以及其他可能的情况 .

  • 50

    值得注意的是,在Angular 1.1.x及更高版本中,您现在可以使用$watchCollection而不是$ watch . 虽然$ watchCollection似乎创建了浅表,但它不能像你期望的那样使用对象数组 . 它可以检测数组的添加和删除,但不能检测数组内对象的属性 .

  • 4

    $watchCollection 完成你想做的事 . 以下是从angularjs网站http://docs.angularjs.org/api/ng/type/$rootScope.Scope复制的示例 . 虽然方便,但需要考虑性能,尤其是在观看大型集合时 .

    $scope.names = ['igor', 'matias', 'misko', 'james'];
      $scope.dataCount = 4;
    
      $scope.$watchCollection('names', function(newNames, oldNames) {
         $scope.dataCount = newNames.length;
      });
    
      expect($scope.dataCount).toEqual(4);
      $scope.$digest();
    
      //still at 4 ... no changes
      expect($scope.dataCount).toEqual(4);
    
      $scope.names.pop();
      $scope.$digest();
    
      //now there's been a change
      expect($scope.dataCount).toEqual(3);
    
  • 509

    以下是使用示例观察范围变量的3种方法的比较:

    $watch() 由以下方式触发:

    $scope.myArray = [];
    $scope.myArray = null;
    $scope.myArray = someOtherArray;
    

    $watchCollection() 由以上所有内容触发:

    $scope.myArray.push({}); // add element
    $scope.myArray.splice(0, 1); // remove element
    $scope.myArray[0] = {}; // assign index to different value
    

    $watch(..., true) 由以上所有内容触发AND:

    $scope.myArray[0].someProperty = "someValue";
    

    JUST ONE MORE THING...

    $watch() 是唯一一个在用另一个数组替换数组时触发的,即使该另一个数组具有相同的确切内容 .

    例如, $watch() 会触发,而 $watchCollection() 则不会:

    $scope.myArray = ["Apples", "Bananas", "Orange" ];
    
    var newArray = [];
    newArray.push("Apples");
    newArray.push("Bananas");
    newArray.push("Orange");
    
    $scope.myArray = newArray;
    

    下面是一个示例JSFiddle的链接,它使用所有不同的监视组合并输出日志消息来指示触发了哪些“监视”:

    http://jsfiddle.net/luisperezphd/2zj9k872/

  • 22

    这个解决方案对我很有用,我在一个指令中这样做:

    范围 . $ watch(attrs.testWatch,function(),true);

    真实的工作做得很好,并对所有的chnages做出反应(添加,删除或修改字段) .

    这是一个可以玩它的工作的plunker .

    Deeply Watching an Array in AngularJS

    我希望这对你有用 . 如果您有任何疑问,请随时提问,我会尽力帮助:)

  • 43

    在我的情况下,我需要观看一个服务,其中包含一个地址对象,也由其他几个控制器监视 . 我被困在一个循环中,直到我添加了'true'参数,这似乎是观看对象时成功的关键 .

    $scope.$watch(function() {
        return LocationService.getAddress();
    }, function(address) {
        //handle address object
    }, true);
    
  • 0

    设置 $watch 函数的 objectEquality 参数(第三个参数)绝对是观察数组所有属性的正确方法 .

    $scope.$watch('columns', function(newVal) {
        alert('columns changed');
    },true); // <- Right here
    

    Piran回答得很好,并提到 $watchCollection .

    More Detail

    我回答一个已经回答的问题的原因是因为我想指出_604458的答案不是很好,不应该使用 .

    问题是摘要不会立即发生 . 他们必须等到当前的代码块完成才能执行 . 因此,观察一个数组的 length 实际上可能会错过 $watchCollection 将捕获的一些重要更改 .

    假设这个配置:

    $scope.testArray = [
        {val:1},
        {val:2}
    ];
    
    $scope.$watch('testArray.length', function(newLength, oldLength) {
        console.log('length changed: ', oldLength, ' -> ', newLength);
    });
    
    $scope.$watchCollection('testArray', function(newArray) {
        console.log('testArray changed');
    });
    

    乍一看,似乎这些会同时发射,例如在这种情况下:

    function pushToArray() {
        $scope.testArray.push({val:3});
    }
    pushToArray();
    
    // Console output
    // length changed: 2 -> 3
    // testArray changed
    

    这很好用,但考虑一下:

    function spliceArray() {
        // Starting at index 1, remove 1 item, then push {val: 3}.
        $testArray.splice(1, 1, {val: 3});
    }
    spliceArray();
    
    // Console output
    // testArray changed
    

    请注意,即使数组有一个新元素并且丢失了一个元素,结果长度也是相同的,所以就像 $watch 一样关注时, length 没有改变 . 不过, $watchCollection 已经接受了 .

    function pushPopArray() {
        $testArray.push({val: 3});
        $testArray.pop();
    }
    pushPopArray();
    
    // Console output
    // testArray change
    

    同一个块中的push和pop也会出现相同的结果 .

    Conclusion

    要观察数组中的每个属性,请在数组iteself上使用包含第三个参数(objectEquality)的 $watch 并设置为true . 是的,这很昂贵,但有时是必要的 .

    要观察对象何时进入/退出数组,请使用 $watchCollection .

    不要在数组的 length 属性上使用 $watch . 我几乎没有理由这样做 .

  • 13
    $scope.changePass = function(data){
        
        if(data.txtNewConfirmPassword !== data.txtNewPassword){
            $scope.confirmStatus = true;
        }else{
            $scope.confirmStatus = false;
        }
    };
    
    <form class="list" name="myForm">
          <label class="item item-input">        
            <input type="password" placeholder="ใส่รหัสผ่านปัจจุบัน" ng-model="data.txtCurrentPassword" maxlength="5" required>
          </label>
          <label class="item item-input">
            <input type="password" placeholder="ใส่รหัสผ่านใหม่" ng-model="data.txtNewPassword" maxlength="5" ng-minlength="5" name="checknawPassword" ng-change="changePass(data)" required>
          </label>
          <label class="item item-input">
            <input type="password" placeholder="ใส่รหัสผ่านใหม่ให้ตรงกัน" ng-model="data.txtNewConfirmPassword" maxlength="5" ng-minlength="5" name="checkConfirmPassword" ng-change="changePass(data)" required>
          </label>      
           <div class="spacer" style="width: 300px; height: 5px;"></div> 
          <span style="color:red" ng-show="myForm.checknawPassword.$error.minlength || myForm.checkConfirmPassword.$error.minlength">รหัสผ่านต้องมีจำนวน 5 หลัก</span><br>
          <span ng-show="confirmStatus" style="color:red">รหัสผ่านใหม่ไม่ตรงกัน</span>
          <br>
          <button class="button button-positive  button-block" ng-click="saveChangePass(data)" ng-disabled="myForm.$invalid || confirmStatus">เปลี่ยน</button>
        </form>
    

相关问题