angularjsで配列を深く監視する方法は?


320

私のスコープにはオブジェクトの配列があります。各オブジェクトのすべての値を監視したいと思います。

これは私のコードです:

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

しかし、値を変更するとき、たとえばに変更TITLEするTITLE2と、alert('columns changed')決してポップされません。

配列内のオブジェクトを詳細に監視する方法は?

ライブデモがあります:http : //jsfiddle.net/SYx9b/

回答:


529

あなたはの第3引数に設定することができます$watchへのtrue

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

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchを参照してください

Angular 1.1.x以降では、$ watchCollectionを使用して、コレクションの浅いウォッチ(「第1レベル」のみ)を見ることができます。

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

https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watchCollectionを参照してください


2
angular.equals3番目の引数がブール値を取るときに使用しているのはなぜですか?
JayQuerie.com 2013

ドキュメントを読み間違えて、Trevorに感謝します。上記のコードを更新し、それに合わせてコードを更新しました。
Piran 2013

36
1.1.xでは現在$watchCollection
Jonathan Rowny 2013

39
$watchCollection私が理解しているように、配列またはオブジェクトの「第1レベル」のみを監視します。上記の答えは、それよりも深く監視する必要がある場合に正解です。bennadel.com/blog/...
Blazemonger

1
すばらしい答え...もし私ができればあなたに複数の賛成票を投じたでしょう... :)ありがとう
ジョニー-Y

50

$ watchのオブジェクトを詳細に調べると、パフォーマンスに影響があります。場合によっては(たとえば、変更がプッシュとポップのみの場合)、array.lengthなどの簡単に計算される値を$ watchしたい場合があります。


1
これはもっと投票すべきです。ディープウォッチングは高価です。OPがディープウォッチングを探していたと思いますが、アレイ自体が変更されたかどうかを知りたいだけの人がここに来るかもしれません。その場合、長さの監視ははるかに速くなります。
Scott Silvi、2013

42
これは回答ではなく、コメントである必要があります。
Blazemonger 2014

1
@Blazemongerコメント(stackoverflow.com/questions/14712089#comment32440226_14713978)で言及されているように、パフォーマンスの問題は$ watchCollectionを使用することで最小限に抑えられます。
Sean the Bean

きちんとしたアドバイス。このコメントは回答ではないので、私の意見では、回答の承認基準を満たすための提案を詳しく説明する方が良いでしょう。このアプローチで、問題に対するエレガントな解決策が達成されると思います。
エイミーペレグリーニ

43

1つの配列のみを監視する場合は、次のコードを使用できます。

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

ただし、これは複数の配列では機能しません。

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

この状況に対処するために、私は通常、監視したい複数の配列をJSONに変換します。

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

@jssebastianがコメントで指摘したように、'$'で始まるメンバーや他のケースも処理できるJSON.stringifyため、よりも望ましいangular.toJson場合があります。


また、に3番目のパラメータがあることもわかりまし$watchた。同じようにできますか?Pass true as a third argument to watch an object's properties too.参照:cheatography.com/proloser/cheat-sheets/angularjs
Freewind

@Freewind複数の配列を監視する必要がある場合は、ここに示すように失敗します。しかし、はい、それでも機能angular.toJsonし、単一の配列で使用するのと同じ機能を提供します。
JayQuerie.com 2013

2
angular.toJsonには '$'で始まるメンバーが含まれていないように見えることに注意してください。angular.toJson({"$ hello": "world"})は単なる "{}"です。代わりにJSON.stringify()を使用しました
jssebastian 2013年

@ jssebastianありがとう-その情報を含めるように回答を更新しました。
JayQuerie.com 2013年

1
どのプロパティが変更されたかを知ることができますか?
アシュウィン2014年

21

Angular 1.1.x 以降では、$ watchではなく$ watchCollectionを使用できることに注意してください。$ watchCollectionは浅いウォッチを作成しているように見えるので、期待どおりのオブジェクトの配列では機能しません。配列への追加と削除は検出できますが、配列内のオブジェクトのプロパティは検出できません。


5
ただし、$ watchCollectionは浅い部分のみを監視します。したがって、私が理解しているように、(質問のように)配列の項目のプロパティを変更しても、イベントはトリガーされません。
Tarnschaf 2013年

3
これは回答ではなく、コメントである必要があります。
Blazemonger 2014

18

スコープ変数を監視できる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/


正確には、特に「もう1つのこと」に注意してください
Andy Ma

12

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

14
OPはオブジェクトの配列を指定しました。あなたの例は文字列の配列で動作しますが、$ watchCollectionはオブジェクトの配列では動作しません。
KevinL 2014年

4

このソリューションは私にとって非常にうまくいきました、私はディレクティブでこれをやっています:

scope。$ watch(attrs.testWatch、function(){.....}、true);

trueはかなりうまく機能し、すべての変更(フィールドの追加、削除、または変更)に反応します。

これは、それで遊ぶための実用的なプランカーです。

AngularJSで配列を深く監視する

これがあなたに役立つことを願っています。ご不明な点がございましたら、お気軽にお問い合わせください。:)


4

私の場合、他のいくつかのコントローラーによって監視されているアドレスオブジェクトを含むサービスを監視する必要がありました。オブジェクトを監視する際の成功の鍵と思われる「true」パラメーターを追加するまで、ループに陥っていました。

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

1

関数のobjectEqualityパラメーター(3番目のパラメーター)を設定する$watchことは、配列のすべてのプロパティを監視するための正しい方法です。

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

ピランはこれに十分に答え、言及$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には、3番目のパラメーター(objectEquality)を含めてtrueに設定して、配列自体でをします。はい、これは高価ですが、時には必要です。

オブジェクトが配列に出入りするときを監視するには、 $watchCollectionます。

配列$watchlengthプロパティでは使用しないでください。私がそうするために考えられる正当な理由はほとんどありません。


0

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.