Regarder plusieurs attributs $ scope


442

Existe-t-il un moyen de s'abonner à des événements sur plusieurs objets en utilisant $watch

Par exemple

$scope.$watch('item1, item2', function () { });

Réponses:


586

À partir d'AngularJS 1.3, il existe une nouvelle méthode appelée $watchGrouppour observer un ensemble d'expressions.

$scope.foo = 'foo';
$scope.bar = 'bar';

$scope.$watchGroup(['foo', 'bar'], function(newValues, oldValues, scope) {
  // newValues array contains the current values of the watch expressions
  // with the indexes matching those of the watchExpression array
  // i.e.
  // newValues[0] -> $scope.foo 
  // and 
  // newValues[1] -> $scope.bar 
});

7
quelle est la différence avec $ watchCollection ('[a, b]') ??
Kamil Tomšík

28
La différence est que vous pouvez utiliser un tableau approprié au lieu d'une chaîne qui ressemble à un tableau
Paolo Moretti

2
J'ai eu un problème similaire, mais en utilisant le modèle controllerAs. Dans mon cas, le correctif consistait à ajouter aux variables le nom du contrôleur:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
morilles

1
Cela ne semble pas fonctionner pour moi sur Angular 1.6. Si je mets un journal de console sur la fonction, je peux voir qu'il ne s'exécute qu'une seule fois avec newValueset oldValuesas undefined, tout en regardant individuellement les propriétés fonctionner comme d'habitude.
Alejandro García Iglesias

290

À partir d'AngularJS 1.1.4, vous pouvez utiliser $watchCollection:

$scope.$watchCollection('[item1, item2]', function(newValues, oldValues){
    // do stuff here
    // newValues and oldValues contain the new and respectively old value
    // of the observed collection array
});

Exemple Plunker ici

Documentation ici


109
Notez que le premier paramètre n'est pas un tableau. C'est une chaîne qui ressemble à un tableau.
MK Safi

1
Merci pour cela. si cela fonctionne pour moi, je vous donnerai mille pièces d'or - virtuelles, bien sûr :)
AdityaSaxena

5
Pour être clair (il m'a fallu quelques minutes pour réaliser), il $watchCollectionest prévu de remettre un objet et de surveiller les modifications de l'une des propriétés de l'objet, alors qu'il $watchGroupest destiné à recevoir un tableau de propriétés individuelles pour surveiller les modifications. Légèrement différent pour s'attaquer à des problèmes similaires mais différents. Phew!
Mattygabe

1
D'une manière ou d'une autre, newValues ​​et oldValues ​​sont toujours égales lorsque vous utilisez cette méthode: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash

Merci. Cela a résolu mon problème. Y a-t-il des problèmes / problèmes de performances liés à l'utilisation de $ watch?
Syed Nasir Abbas

118

$watch le premier paramètre peut également être une fonction.

$scope.$watch(function watchBothItems() {
  return itemsCombinedValue();
}, function whenItemsChange() {
  //stuff
});

Si vos deux valeurs combinées sont simples, le premier paramètre n'est normalement qu'une expression angulaire. Par exemple, firstName et lastName:

$scope.$watch('firstName + lastName', function() {
  //stuff
});

8
Cela ressemble à un cas d'utilisation qui demande par défaut d'être inclus dans le cadre. Même une propriété calculée aussi simple que fullName = firstName + " " + lastNamenécessite de passer une fonction comme premier argument (plutôt qu'une simple expression comme 'firstName, lastName'). Angular est SI puissant, mais il y a certains domaines où il peut être énormément plus convivial pour les développeurs d'une manière qui ne semble pas compromettre les performances ou les principes de conception.
XML

4
Eh bien $ watch prend juste une expression angulaire, qui est évaluée sur la portée à laquelle elle est appelée. Vous pourriez donc faire $scope.$watch('firstName + lastName', function() {...}). Vous pouvez aussi simplement faire deux montres: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. J'ai ajouté le cas simple à la réponse.
Andrew Joslin

Le plus dans 'firstName + lastName' est la concaténation de chaînes. Donc, angulaire vérifie simplement si le résultat de la concaténation est différent maintenant.
mb21

16
La concaténation de chaînes nécessite un peu de soin - si vous changez "paran orman" en "para norman", vous ne déclencherez pas de réponse; mieux de mettre un séparateur comme "\ t | \ t" entre le premier et le deuxième mandat, ou simplement s'en tenir à JSON qui est définitif
Dave Edelhart

bien que amberjs suce, mais il a cette fonctionnalité intégrée! entendre que les gars anguleux. Je suppose que faire des montres est le moyen le plus rationnel.
Aladdin Mhemed

70

Voici une solution très similaire à votre pseudo-code d'origine qui fonctionne réellement:

$scope.$watch('[item1, item2] | json', function () { });

EDIT: D'accord, je pense que c'est encore mieux:

$scope.$watch('[item1, item2]', function () { }, true);

Fondamentalement, nous sautons l'étape json, qui semblait stupide au début, mais cela ne fonctionnait pas sans elle. Leur clé est le 3e paramètre souvent omis qui active l'égalité d'objet par opposition à l'égalité de référence. Ensuite, les comparaisons entre nos objets de tableau créés fonctionnent correctement.


J'avais une question similaire. Et c'est la bonne réponse pour moi.
Calvin Cheng

Les deux ont une légère surcharge de performances si les éléments de l'expression de tableau ne sont pas des types simples.
null

C'est vrai .. il fait une comparaison complète des objets composants. Ce qui peut ou non être ce que vous voulez. Voir la réponse actuellement approuvée pour une version utilisant un angulaire moderne qui ne fait pas de comparaison approfondie.
Karen Zilles

Pour une raison quelconque, lorsque j'essayais d'utiliser $ watchCollection sur un tableau, j'ai eu cette erreur TypeError: Object #<Object> has no method '$watchCollection'mais cette solution m'aide à résoudre mon problème!
abottoni

Cool, vous pouvez même regarder des valeurs dans des objets:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Serge van den Oever

15

Vous pouvez utiliser des fonctions dans $ watchGroup pour sélectionner des champs d'un objet dans la portée.

        $scope.$watchGroup(
        [function () { return _this.$scope.ViewModel.Monitor1Scale; },   
         function () { return _this.$scope.ViewModel.Monitor2Scale; }],  
         function (newVal, oldVal, scope) 
         {
             if (newVal != oldVal) {
                 _this.updateMonitorScales();
             }
         });

1
Pourquoi utilisez-vous _this? La portée n'est-elle pas portée dans les fonctions sans la définir explicitement?
sg.cc

1
J'utilise tapuscrit, le code présenté est le résultat compilé.
Yang Zhang

12

Pourquoi ne pas simplement l'envelopper dans un forEach?

angular.forEach(['a', 'b', 'c'], function (key) {
  scope.$watch(key, function (v) {
    changed();
  });
});

C'est à peu près la même chose que de fournir une fonction pour la valeur combinée, sans avoir à se soucier de la composition de la valeur .


Dans ce cas, log () serait exécuté plusieurs fois, non? Je pense qu'en cas de fonctions $ watch imbriquées, ce ne serait pas le cas. Et cela ne devrait pas être la solution pour regarder plusieurs attributs à la fois.
John Doe

Non, le journal n'est exécuté qu'une fois par modification par propriété surveillée. Pourquoi serait-il invoqué plusieurs fois?
Erik Aigner

Bon, je veux dire que ça ne marcherait pas bien si nous voulions tous les regarder simultanément.
John Doe

1
Bien sûr, pourquoi pas? Je le fais de cette façon dans un de mes projets. changed()est invoqué chaque fois que quelque chose change dans l'une des propriétés. C'est exactement le même comportement que si une fonction pour la valeur combinée était fournie.
Erik Aigner

1
C'est légèrement différent en ce que si plusieurs des éléments du tableau changent en même temps, $ watch ne déclenchera le gestionnaire qu'une seule fois au lieu d'une fois par élément modifié.
bingles

11

Une solution légèrement plus sûre pour combiner des valeurs pourrait être d'utiliser la fonction suivante comme $watchfonction:

function() { return angular.toJson([item1, item2]) }

ou

$scope.$watch(
  function() {
    return angular.toJson([item1, item2]);
  },
  function() {
    // Stuff to do after either value changes
  });

5

Le paramètre $ watch first peut être une expression ou une fonction angulaire . Voir la documentation sur $ scope. $ Watch . Il contient de nombreuses informations utiles sur le fonctionnement de la méthode $ watch: lorsque watchExpression est appelée, comment angulaire compare les résultats, etc.


4

que diriez-vous:

scope.$watch(function() { 
   return { 
      a: thing-one, 
      b: thing-two, 
      c: red-fish, 
      d: blue-fish 
   }; 
}, listener...);

2
Sans le troisième trueparamètre to scope.$watch(comme dans votre exemple), listener()se déclencherait à chaque fois, car le hachage anonyme a une référence différente à chaque fois.
Peter V. Mørch

J'ai trouvé cette solution adaptée à ma situation. Pour moi, il était plus comme: return { a: functionA(), b: functionB(), ... }. Je vérifie ensuite les objets retournés des nouvelles et anciennes valeurs les uns par rapport aux autres.
RevNoah

4
$scope.$watch('age + name', function () {
  //called when name or age changed
});

Ici, la fonction sera appelée lorsque l'âge et la valeur du nom seront modifiés.


2
Assurez-vous également dans ce cas de ne pas utiliser les arguments newValue et oldValue qui passent angulaires dans le rappel car ils seront la concaténation résultante et pas seulement le champ qui a été modifié
Akash Shinde

1

Angular introduit $watchGroupdans la version 1.3 à l'aide duquel nous pouvons regarder plusieurs variables, avec un seul $watchGroupbloc $watchGroupprend le tableau comme premier paramètre dans lequel nous pouvons inclure toutes nos variables à regarder.

$scope.$watchGroup(['var1','var2'],function(newVals,oldVals){
   console.log("new value of var1 = " newVals[0]);
   console.log("new value of var2 = " newVals[1]);
   console.log("old value of var1 = " oldVals[0]);
   console.log("old value of var2 = " oldVals[1]);
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.