提问



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


这是我的代码:


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/[41]

最佳参考




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


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


见https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch [42]


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


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


见https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollection[43]

其它参考1


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

其它参考2


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


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


示例[44]


但这不适用于多个数组:


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


示例[45]


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


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


示例[46]


正如@jssebastian在评论中指出的那样,JSON.stringify可能优于angular.toJson,因为它可以处理以$开头的成员以及可能的其他案例。

其它参考3


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

其它参考4


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


  $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);

其它参考5


以下是使用示例观察范围变量的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)由以上所有内容触发:


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


只是一件事......


$ 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/[49]

其它参考6


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


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


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


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


深入观察AngularJS中的数组[50]


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

其它参考7


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


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

其它参考8


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


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


皮兰回答得很好,并提到$watchCollection


更多细节


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


问题是摘要不会立即发生。他们必须等到当前的代码块完成才能执行。因此,观察数组的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也会出现相同的结果。


结论


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


要观察对象何时进入/退出阵列,请使用$watchCollection


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