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

Posted

技术标签:

【中文标题】如何深入观察angularjs中的数组?【英文标题】:How to deep watch an array in angularjs? 【发布时间】:2013-01-20 15:34:22 【问题描述】:

我的作用域中有一个对象数组,我想查看每个对象的所有值。

这是我的代码:

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/

【问题讨论】:

【参考方案1】:

您可以将$watch的第三个参数设置为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

【讨论】:

当第三个参数采用boolean value 时,为什么要使用angular.equals 感谢 Trevor,误读了文档。我已经更新了上面的代码,并更新了我的代码以匹配。 在 1.1.x 中您现在拥有 $watchCollection $watchCollection 据我了解,只会观察数组或对象的“第一级”。如果您需要更深入地观察,上述答案是正确的。 bennadel.com/blog/… 很好的答案......如果可以的话,我会给你不止一票...... :) 谢谢【参考方案2】:

在 $watch 中深入挖掘对象会产生性能影响。有时(例如,当更改只是推送和弹出时),您可能想要 $watch 一个容易计算的值,例如 array.length。

【讨论】:

这应该有更多的选票。深度观察是昂贵的。我知道 OP 正在寻找深度观察,但人们可能只是想知道阵列本身是否发生了变化。在这种情况下,观看长度要快得多。 这应该是评论,而不是答案。 如@Blazemonger 评论 (***.com/questions/14712089#comment32440226_14713978) 所述,使用 $watchCollection 可以最大限度地减少性能问题。 简洁的建议。关于此评论不是答案,我认为最好详细说明满足答案的接受标准的建议。我认为通过这种方法可以很好地解决这个问题。【参考方案3】:

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

$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 在 cmets 中指出的那样,JSON.stringify 可能比 angular.toJson 更可取,因为它可以处理以“$”开头的成员以及可能的其他情况。

【讨论】:

我还发现$watch 有第三个参数,它可以做同样的事情吗? Pass true as a third argument to watch an object's properties too. 见:cheatography.com/proloser/cheat-sheets/angularjs @Freewind 如果您需要监视多个数组,它将失败为seen here。但是,是的,这也可以工作,并提供与在单个阵列上使用 angular.toJson 相同的功能。 请注意,angular.toJson 似乎不包含以“$”开头的包含成员:​​angular.toJson("$hello":"world") 只是“”。我使用 JSON.stringify() 作为替代 @jssebastian 谢谢 - 我已经更新了答案以包含该信息。 我们能知道哪个属性发生了变化吗?【参考方案4】:

值得注意的是,在 Angular 1.1.x 及更高版本中,您现在可以使用 $watchCollection 而不是 $watch。尽管 $watchCollection 似乎创建了浅表,因此它不会像您期望的那样处理对象数组。它可以检测到数组的增删改查,但不能检测数组内对象的属性。

【讨论】:

$watchCollection 只观察浅层,然而。据我了解,更改数组项的属性(如问题所示)不会触发事件。 这应该是评论,而不是答案。【参考方案5】:

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

$watch() 由以下触发:

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

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

$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/

【讨论】:

没错,特别是要注意“更多的事情”【参考方案6】:

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

【讨论】:

OP 指定了一个对象数组。您的示例适用于字符串数组,但 $watchCollection 不适用于对象数组。【参考方案7】:

这个解决方案对我来说效果很好,我在指令中这样做:

scope.$watch(attrs.testWatch, function() ....., true);

true 效果很好,并且可以对所有更改(添加、删除或修改字段)做出反应。

这是一个可以玩的工作小工具。

Deeply Watching an Array in AngularJS

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

【讨论】:

【参考方案8】:

在我的例子中,我需要监视一个服务,它包含一个地址对象,也被其他几个控制器监视。在添加 'true' 参数之前,我一直陷入循环,这似乎是观察对象时成功的关键。

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

【讨论】:

【参考方案9】:

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

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

Piran 很好地回答了这个问题,并提到了$watchCollection

更多详情

我之所以回答一个已经回答的问题,是因为我想指出wizardwerdna 的回答不好,不应该使用。

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

在同一个块中推送和弹出会发生相同的结果。

结论

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

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

不要在数组的length 属性上使用$watch。我想不出这样做的理由。

【讨论】:

【参考方案10】:

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

【讨论】:

以上是关于如何深入观察angularjs中的数组?的主要内容,如果未能解决你的问题,请参考以下文章

列表中的项目数过滤 AngularJS

AngularJS API

如何在 AngularJS 中收听 jQuery 事件

AngularJS:如何观察服务变量?

AngularJS + OAuth

AngularJS中模块之间的通信