Guarda più attributi $ scope


Risposte:


586

A partire da AngularJS 1.3 c'è un nuovo metodo chiamato $watchGroupper osservare una serie di espressioni.

$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
qual è la differenza rispetto a $ watchCollection ('[a, b]') ??
Kamil Tomšík,

28
La differenza è che puoi usare un array adeguato invece di una stringa che assomiglia ad un array
Paolo Moretti,

2
Ho avuto un problema simile ma usando il modello controllerAs. Nel mio caso la correzione consisteva nel anteporre alle variabili il nome del controller:$scope.$watchGroup(['mycustomctrl.foo', 'mycustomctrl.bar'],...
spugnole

1
Questo non sembra funzionare per me su Angular 1.6. Se inserisco un log di console nella funzione, vedo che viene eseguito una sola volta con newValuese oldValuescome undefined, mentre individualmente le proprietà funzionano come al solito.
Alejandro García Iglesias,

290

A partire da AngularJS 1.1.4 è possibile utilizzare $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
});

Esempio di Plunker qui

Documentazione qui


109
Si noti che il primo parametro non è un array. È una stringa che sembra un array.
MK Safi,

1
grazie per questo. se funziona per me, ti darò un migliaio di monete d'oro - quelle virtuali, ovviamente :)
AdityaSaxena

5
Per essere chiari (mi ci sono voluti alcuni minuti per rendermi conto) $watchCollectionè destinato a consegnare un oggetto e fornire un orologio per le modifiche a qualsiasi proprietà dell'oggetto, mentre $watchGroupè destinato a essere consegnato una serie di singole proprietà a cui prestare attenzione per le modifiche. Leggermente diverso per affrontare problemi simili ma diversi. Accidenti!
Mattygabe,

1
In qualche modo, newValues ​​e oldValues ​​sono sempre uguali quando si utilizza questo metodo: plnkr.co/edit/SwwdpQcNf78pQ80hhXMr
mostruash

Grazie. Ha risolto il mio problema. C'è qualche hit / problema di prestazioni nell'uso di $ watch?
Syed Nasir Abbas,

118

$watch il primo parametro può anche essere una funzione.

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

Se i due valori combinati sono semplici, il primo parametro è normalmente solo un'espressione angolare. Ad esempio, firstName e lastName:

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

8
Sembra un caso d'uso che richiede per impostazione predefinita l'inclusione nel framework. Anche una proprietà calcolata semplice quanto fullName = firstName + " " + lastNamerichiede il passaggio di una funzione come primo argomento (anziché una semplice espressione come 'firstName, lastName'). Angular è così potente, ma ci sono alcune aree in cui può essere enormemente più favorevole agli sviluppatori in modi che non sembrano compromettere le prestazioni o i principi di progettazione.
XML

4
Bene $ watch prende solo un'espressione angolare, che viene valutata in base all'ambito su cui è chiamata. Quindi potresti farlo $scope.$watch('firstName + lastName', function() {...}). Si potrebbe anche solo fare due orologi: function nameChanged() {}; $scope.$watch('firstName', nameChanged); $scope.$watch('lastName', nameChanged);. Ho aggiunto il semplice caso alla risposta.
Andrew Joslin,

Il vantaggio in 'firstName + lastName' è la concatenazione di stringhe. Così angolare controlla solo se il risultato della concatenazione è diverso ora.
mb21

16
La concatenazione di stringhe richiede un po 'di attenzione - se si cambia "paran orman" in "para norman" di quanto non si inneschi una risposta; meglio mettere un divisore come "\ t | \ t" tra il primo e il secondo termine, o semplicemente attenersi a JSON che è definitivo
Dave Edelhart,

sebbene amberjs faccia schifo, ma ha questa funzione integrata! senti quei ragazzi angolari. Immagino che fare degli orologi sia il modo più razionale possibile.
Aladdin Mhemed,

70

Ecco una soluzione molto simile al tuo pseudo-codice originale che funziona davvero:

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

EDIT: Okay, penso che sia ancora meglio:

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

Fondamentalmente stiamo saltando il passo json, che all'inizio sembrava stupido, ma non funzionava senza di essa. La chiave è il terzo parametro spesso omesso che attiva l'uguaglianza degli oggetti rispetto all'uguaglianza di riferimento. Quindi i confronti tra i nostri oggetti array creati in realtà funzionano correttamente.


Ho avuto una domanda simile. E questa è la risposta corretta per me.
Calvin Cheng,

Entrambi hanno un leggero sovraccarico di prestazioni se gli elementi nell'espressione della matrice non sono tipi semplici.
null,

Questo è vero ... sta facendo un confronto approfondito degli oggetti componenti. Quale può o meno essere quello che vuoi. Vedi la risposta attualmente approvata per una versione che utilizza un moderno angolare che non esegue un confronto approfondito.
Karen Zilles,

Per qualche ragione, quando stavo cercando di usare $ watchCollection su un array, ho riscontrato questo errore TypeError: Object #<Object> has no method '$watchCollection'ma questa soluzione mi aiuta a risolvere il mio problema!
Abottoni,

Bene, puoi persino guardare i valori all'interno degli oggetti:$scope.$watch('[chaptercontent.Id, anchorid]', function (newValues, oldValues) { ... }, true);
Serge van den Oever,

15

È possibile utilizzare le funzioni in $ watchGroup per selezionare i campi di un oggetto nell'ambito.

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

1
Perché si usa _this? L'ambito non viene trasferito nelle funzioni senza definirlo esplicitamente?
sg.cc,

1
Uso il dattiloscritto, il codice presentato è il risultato compilato.
Yang Zhang,

12

Perché non semplicemente avvolgerlo in un forEach?

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

Si tratta dello stesso overhead di fornire una funzione per il valore combinato, senza doversi preoccupare della composizione del valore .


In questo caso log () verrebbe eseguito più volte, giusto? Penso che in caso di funzioni $ watch nidificate non lo farebbe. E non dovrebbe essere nella soluzione per guardare più attributi contemporaneamente.
John Doe,

No, il registro viene eseguito una sola volta per modifica per proprietà osservata. Perché dovrebbe essere invocato più volte?
Erik Aigner,

Bene, voglio dire che non funzionerebbe bene se vogliamo guardarli tutti contemporaneamente.
John Doe,

1
Certo, perché no? Lo faccio in un mio progetto. changed()viene invocato ogni volta che qualcosa cambia in una delle proprietà. Questo è esattamente lo stesso comportamento di una funzione per il valore combinato.
Erik Aigner,

1
È leggermente diverso dal fatto che se più degli oggetti nell'array cambiano contemporaneamente l'orologio $ lancerà il gestore solo una volta anziché una volta per ogni oggetto modificato.
brontola il

11

Una soluzione leggermente più sicura per combinare i valori potrebbe essere quella di utilizzare quanto segue come $watchfunzione:

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

o

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

5

$ watch primo parametro può essere espressione o funzione angolare . Vedi la documentazione su $ scope. $ Watch . Contiene molte informazioni utili su come funziona il metodo $ watch: quando viene chiamato watchExpression, come angolare confronta i risultati, ecc.


4

che ne dite di:

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

2
Senza il terzo trueparametro da scope.$watch(come nel tuo esempio), listener()verrebbe attivato ogni volta, perché l'hash anonimo ha un riferimento diverso ogni volta.
Peter V. Mørch,

Ho trovato che questa soluzione ha funzionato per la mia situazione. Per me, era più come: return { a: functionA(), b: functionB(), ... }. Quindi controllo gli oggetti restituiti dei valori vecchi e nuovi uno contro l'altro.
RevNoah,

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

Qui la funzione verrà chiamata quando vengono cambiati sia il valore di età che il nome.


2
Anche in questo caso assicurati di non usare gli argomenti newValue e oldValue che angolare passa nel callback perché saranno la concatenazione risultante e non solo il campo che è stato modificato
Akash Shinde

1

Angular introdotto $watchGroupnella versione 1.3 con cui possiamo guardare più variabili, con un singolo $watchGroupblocco $watchGroupprende l'array come primo parametro in cui possiamo includere tutte le nostre variabili da guardare.

$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]);
});
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.