web-dev-qa-db-fra.com

Comment regarder en profondeur un tableau dans angularjs?

Il y a un tableau d'objets dans ma portée, je veux regarder toutes les valeurs de chaque objet.

Ceci est mon code:

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

Mais lorsque je modifie les valeurs, par exemple Je change TITLE en TITLE2, la alert('columns changed') ne s'est jamais affichée.

Comment regarder en profondeur les objets dans un tableau?

Il y a une démo en direct: http://jsfiddle.net/SYx9b/

316
Freewind

Vous pouvez définir le 3ème argument de $watch sur true:

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

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

Depuis Angular 1.1.x, vous pouvez également utiliser $ watchCollection pour regarder une surveillance superficielle (le "premier niveau" de) de la collection.

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

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

521
Piran

Plonger profondément un objet dans votre montre a des conséquences sur les performances. Parfois (par exemple, lorsque les modifications ne sont que des push et des pops), vous pouvez vouloir regarder une valeur facilement calculée, telle que array.length.

50
wizardwerdna

Si vous ne regardez qu'un seul tableau, vous pouvez simplement utiliser ce morceau de code:

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

exemple

Mais cela ne fonctionnera pas avec plusieurs tableaux:

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

exemple

Pour gérer cette situation, je convertis généralement les tableaux multiples que je souhaite surveiller en JSON:

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

exemple

Comme @jssebastian l'a souligné dans les commentaires, JSON.stringify peut être préférable à angular.toJson, car il peut gérer les membres commençant par '$' et éventuellement d'autres cas.

43
JayQuerie.com

Il est à noter que dans Angular 1.1.x et versions ultérieures, vous pouvez maintenant utiliser $ watchCollection plutôt que $ watch. Bien que $ watchCollection semble créer des surveillances superficielles, il ne fonctionnera pas avec des tableaux d'objets comme vous le souhaitez. Il peut détecter les ajouts et les suppressions dans le tableau, mais pas les propriétés des objets à l'intérieur des tableaux.

21
Jonathan Rowny

Voici une comparaison des 3 façons dont vous pouvez regarder une variable de portée avec des exemples:

$ watch () est déclenché par:

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

$ watchCollection () est déclenché par tout ce qui précède ET:

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

$ watch (..., true) est déclenché par TOUT ci-dessus ET:

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

JUSTE UNE CHOSE DE PLUS ...

$ watch () est le seul déclencheur lorsqu'un tableau est remplacé par un autre, même si cet autre a exactement le même contenu.

Par exemple, où $watch() se déclenche et $watchCollection() ne:

$scope.myArray = ["Apples", "Bananas", "Orange" ];

var newArray = [];
newArray.Push("Apples");
newArray.Push("Bananas");
newArray.Push("Orange");

$scope.myArray = newArray;

Vous trouverez ci-dessous un lien vers un exemple de JSFiddle qui utilise les différentes combinaisons de surveillance et génère des messages de journal pour indiquer les "surveillances" déclenchées:

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

17
Luis Perez

$ watchCollection accomplit ce que vous voulez faire. Vous trouverez ci-dessous un exemple copié du site Web angularjs http://docs.angularjs.org/api/ng/type/$rootScope.Scope Bien que ce soit pratique, la performance doit être prise en compte en particulier lorsque vous regarder une grande collection.

  $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

Dans mon cas, je devais surveiller un service, qui contenait un objet adresse également surveillé par plusieurs autres contrôleurs. J'étais bloqué dans une boucle jusqu'à ce que j'ajoute le paramètre "true", qui semble être la clé du succès lorsque vous regardez des objets.

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

Cette solution a très bien fonctionné pour moi, je le fais dans une directive:

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

le vrai fonctionne plutôt bien et réagit à tous les changements (ajouter, supprimer ou modifier un champ).

Voici un plunker de travail pour jouer avec elle.

Regardant profondément un tableau dans AngularJS

J'espère que cela peut vous être utile. Si vous avez des questions, n'hésitez pas à demander, je vais essayer de vous aider :)

4
EPotignano

Le réglage du paramètre objectEquality (troisième paramètre) de la fonction $watch est sans aucun doute le moyen correct de regarder TOUTES les propriétés du tableau.

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

Piran y répond assez bien et mentionne $watchCollection également.

Plus de détails

La raison pour laquelle je réponds à une question à laquelle vous avez déjà répondu est parce que je tiens à souligner que la réponse de wizardwerdna n'est pas bonne et qu'elle ne devrait pas être utilisée.

Le problème est que les résumés ne se produisent pas immédiatement. Ils doivent attendre la fin du bloc de code actuel avant de s'exécuter. Par conséquent, observez que la length d'un tableau peut manquer des modifications importantes que $watchCollection va intercepter.

Supposons cette configuration:

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

À première vue, il peut sembler que ceux-ci se déclenchent en même temps, comme dans ce cas:

function pushToArray() {
    $scope.testArray.Push({val:3});
}
pushToArray();

// Console output
// length changed: 2 -> 3
// testArray changed

Cela fonctionne assez bien, mais considérez ceci:

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

// Console output
// testArray changed

Notez que la longueur résultante est la même, même si le tableau comporte un nouvel élément et en perd un, ainsi, comme le $watch est concerné, length n'a pas changé. $watchCollection l'a bien compris.

function pushPopArray() {
    $testArray.Push({val: 3});
    $testArray.pop();
}
pushPopArray();

// Console output
// testArray change

Le même résultat se produit avec un Push et un Pop dans le même bloc.

Conclusion

Pour surveiller chaque propriété du tableau, utilisez un $watch sur le tableau lui-même avec le troisième paramètre (objectEquality) inclus et défini sur true. Oui, c'est cher mais parfois nécessaire.

Pour regarder quand un objet entre/sort du tableau, utilisez un $watchCollection.

N'utilisez PAS un $watch sur la propriété length du tableau. Il n'y a presque aucune bonne raison à laquelle je peux penser de le faire.

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