web-dev-qa-db-ja.com

Angularjsで配列を詳細に監視する方法

私の範囲内のオブジェクトの配列があります、私は各オブジェクトのすべての値を見たいです。

これは私のコードです:

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

しかし、値を変更すると、 TITLETITLE2に変更しましたが、alert('columns changed')はポップされませんでした。

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

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

312
Freewind

$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 を参照してください。

516
Piran

$ watchでオブジェクトを深く掘り下げると、パフォーマンスが低下します。時々(例えば、変更がプッシュとポップのみである場合)、array.lengthのような簡単に計算された値を$監視したいかもしれません。

50
wizardwerdna

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.stringifyangular.toJsonよりも好まれるかもしれません。

43
JayQuerie.com

Angular 1.1.x以降では、$ watchではなく $ watchCollection を使用できるようになりました。 $ watchCollectionは浅い時計を作成するように見えますが、あなたが期待するようなオブジェクトの配列では動作しません。配列への追加や削除は検出できますが、配列内のオブジェクトのプロパティは検出できません。

21
Jonathan Rowny

以下は、スコープ変数を見ることができる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";

あと1つだけ...

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

17
Luis Perez

$ watchCollectionは、やりたいことを達成します。以下は、angularjsのWebサイトからコピーした例です 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);
13
Jin

私の場合は、他のいくつかのコントローラによって監視されているアドレスオブジェクトを含むサービスを監視する必要がありました。 'true'パラメータを追加するまでループし続けました。これは、オブジェクトを見ているときに成功するための鍵となるようです。

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

この解決策は私にとって非常にうまくいった、私はディレクティブの中でこれをやっている。

$ watch(attrs.testWatch、function(){.....}、true);スコープ。

真実はかなりうまくいき、すべての変更に対して反応します(フィールドの追加、削除、または変更)。

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

AngularJSで配列を詳しく見る

これがあなたに役立つことを願っています。ご質問がありましたら、お気軽にお尋ねください。:)

4
EPotignano

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

$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

同じ結果がプッシュとポップでも同じブロックで発生します。

結論

配列内のすべてのプロパティを監視するには、3番目のパラメータ(objectEquality)を含めて、配列自体に$watchを使用し、trueに設定します。はい、これは高価ですが、時には必要です。

オブジェクトがいつ配列に出入りするかを見るには、$watchCollectionを使用します。

配列のlengthプロパティに$watchを使用しないでください。私がそう考えることができるもっともな理由はほとんどありません。

$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>
0
Fluke Klumame