Come uso $ scope. $ Watch e $ scope. $ Si applicano in AngularJS?


1088

Non capisco come usare $scope.$watche $scope.$apply. La documentazione ufficiale non è utile.

Quello che non capisco in modo specifico:

  • Sono collegati al DOM?
  • Come posso aggiornare le modifiche DOM al modello?
  • Qual è il punto di connessione tra di loro?

Ho provato questo tutorial , ma ci vuole la comprensione $watche $applyper scontato.

Cosa faccio $applye $watchcosa faccio e come li uso in modo appropriato?

Risposte:


1737

Devi essere consapevole di come funziona AngularJS per capirlo.

Ciclo digest e $ scope

Innanzitutto, AngularJS definisce il concetto di un cosiddetto ciclo digest . Questo ciclo può essere considerato come un ciclo, durante il quale AngularJS verifica se vi sono modifiche a tutte le variabili osservate da tutti $scopei messaggi. Quindi, se hai $scope.myVardefinito nel tuo controller e questa variabile è stata contrassegnata per essere guardata , allora stai implicitamente dicendo ad AngularJS di monitorare le modifiche su myVarogni iterazione del loop.

Una domanda di follow-up naturale sarebbe: tutto è attaccato $scopeall'essere guardato? Fortunatamente no. Se osservassi le modifiche di ogni oggetto nel tuo $scope, allora un ciclo digest richiederebbe tempi lunghi per essere valutato e potresti incorrere rapidamente in problemi di prestazioni. Ecco perché il team di AngularJS ci ha fornito due modi per dichiararne alcuni$scope variabili come osservate (leggi sotto).

$ watch aiuta ad ascoltare le modifiche di $ scope

Esistono due modi per dichiarare una $scopevariabile come osservata.

  1. Usandolo nel tuo modello tramite l'espressione <span>{{myVar}}</span>
  2. Aggiungendolo manualmente tramite il $watchservizio

Annuncio 1) Questo è lo scenario più comune e sono sicuro che lo hai già visto prima, ma non sapevi che questo ha creato un orologio in background. Sì, aveva! Utilizzo delle direttive AngularJS (comeng-repeat ) può anche creare orologi impliciti.

Annuncio 2) Ecco come crei i tuoi orologi . $watchil servizio ti aiuta a eseguire un po 'di codice quando $scopeè cambiato un valore associato a . Viene usato raramente, ma a volte è utile. Ad esempio, se si desidera eseguire alcuni codici ogni volta che "myVar" cambia, è possibile effettuare le seguenti operazioni:

function MyController($scope) {

    $scope.myVar = 1;

    $scope.$watch('myVar', function() {
        alert('hey, myVar has changed!');
    });

    $scope.buttonClicked = function() {
        $scope.myVar = 2; // This will trigger $watch expression to kick in
    };
}

$ apply consente di integrare le modifiche con il ciclo digest

Puoi pensare alla $applyfunzione come a un meccanismo di integrazione . Vedete, ogni volta che cambiate alcune variabili osservate associate a$scope all'oggetto, AngularJS saprà che la modifica è avvenuta. Questo perché AngularJS sapeva già monitorare tali cambiamenti. Quindi, se accade nel codice gestito dal framework, il ciclo digest proseguirà.

Tuttavia, a volte si desidera modificare un valore al di fuori del mondo AngularJS e vedere i cambiamenti propagarsi normalmente. Considera questo: hai un $scope.myVarvalore che verrà modificato nel $.ajax()gestore di un jQuery . Questo accadrà ad un certo punto in futuro. AngularJS non vede l'ora che ciò accada, poiché non è stato richiesto di attendere su jQuery.

Per affrontare questo, $apply è stato introdotto. Ti consente di avviare esplicitamente il ciclo di digestione. Tuttavia, è consigliabile utilizzarlo solo per migrare alcuni dati su AngularJS (integrazione con altri framework), ma non utilizzare mai questo metodo combinato con il normale codice AngularJS, poiché AngularJS genererà un errore.

In che modo tutto ciò è legato al DOM?

Bene, dovresti davvero seguire di nuovo il tutorial, ora che sai tutto questo. Il ciclo digest assicurerà che l'interfaccia utente e il codice JavaScript rimangano sincronizzati, valutando ogni watcher collegato a tutti $scopei messaggi finché non cambia nulla. Se non si verificano più modifiche nel ciclo digest, viene considerato finito.

È possibile allegare oggetti $scopeall'oggetto esplicitamente nel Controller o dichiarandoli nella {{expression}}forma direttamente nella vista.

Spero che ciò aiuti a chiarire alcune conoscenze di base su tutto ciò.

Ulteriori letture:


57
"Angular controlla se ci sono cambiamenti a tutte le variabili associate a tutti i $ scopes" - Non penso che sia del tutto giusto. Credo che solo Angular (sporco) controlli le proprietà $ scope che hanno impostato $ watch (si noti che l'uso di {{}} in una vista creerà automaticamente $ watch). Vedi anche la sezione "Considerazioni sulle prestazioni di Scope $ watch" nella pagina Scope .
Mark Rajcok,

5
Questo potrebbe essere il caso. Proverò a trovare un po 'di tempo per leggere di più e modificare la mia risposta.
ŁukaszBachman,

15
@MarkRajcok, avevi ragione. Ho modificato la mia risposta e fatto notare un articolo che mostra bene come viene implementato.
ŁukaszBachman,

3
che ne dici di usarlo? (Metodo "Controllo come")
Leandro

2
L'uso di "Controllo come" non dovrebbe avere alcun impatto sulle informazioni di cui sopra. L'uso di this.myVar mette myVar nell'ambito.
Marcus Rådell,

161

In AngularJS, aggiorniamo i nostri modelli e le nostre viste / modelli aggiornano il DOM "automaticamente" (tramite direttive integrate o personalizzate).

$ apply e $ watch, entrambi essendo metodi Scope, non sono correlati al DOM.

La pagina Concepts (sezione "Runtime") ha una spiegazione abbastanza buona del ciclo $ digest, $ apply, la coda $ evalAsync e la $ watch list. Ecco l'immagine che accompagna il testo:

$ digest loop

Qualunque codice abbia accesso a un ambito - normalmente i controller e le direttive (le loro funzioni di collegamento e / o i loro controller) - possono impostare una " watchExpression " che AngularJS valuterà rispetto a tale ambito. Questa valutazione si verifica ogni volta che AngularJS entra nel suo ciclo $ digest (in particolare, il ciclo "$ watch list"). Puoi guardare le singole proprietà dell'ambito, puoi definire una funzione per guardare insieme due proprietà, puoi vedere la lunghezza di un array, ecc.

Quando le cose accadono "all'interno di AngularJS" - ad esempio, si digita in una casella di testo in cui è abilitato il databinding bidirezionale di AngularJS (ovvero utilizza il modello ng), viene richiamato un callback $ http, ecc. - $ apply è già stato chiamato, quindi abbiamo sei all'interno del rettangolo "AngularJS" nella figura sopra. Verranno valutate tutte le watchExpressions (possibilmente più di una volta - fino a quando non verranno rilevate ulteriori modifiche).

Quando le cose accadono "al di fuori di AngularJS" - ad esempio, hai usato bind () in una direttiva e poi quell'evento viene generato, provocando il richiamo del callback o alcuni fuochi di callback registrati jQuery - siamo ancora nel rettangolo "Nativo". Se il codice di callback modifica qualcosa che sta guardando qualsiasi $ watch, chiama $ apply per entrare nel rettangolo AngularJS, facendo funzionare il ciclo $ digest, e quindi AngularJS noterà il cambiamento e farà la sua magia.


5
Capisco l'idea, quello che non capisco è come i dati vengono effettivamente trasferiti. Ho un modello che è un oggetto con molti dati, ne uso alcuni per manipolare il DOM. poi alcuni di essi vengono cambiati. Come posso inserire i dati modificati nel posto giusto nel modello? Nell'esempio che ho usato, fa la manipolazione e alla fine usa semplicemente scope.$apply(scope.model), non capisco quali dati vengono trasferiti e come vengono trasferiti nel posto giusto nel modello?
ilyo,

6
Non è in corso alcun trasferimento di dati magico. Normalmente con le app Angular, è necessario modificare i modelli Angular, che guidano quindi gli aggiornamenti vista / DOM. Se aggiorni il DOM al di fuori di Angular, dovrai aggiornare manualmente i modelli. scope.$apply(scope.model)valuterà semplicemente scope.modelcome espressione angolare, quindi entrerà in un ciclo $ digest. Nell'articolo a cui fai riferimento, probabilmente scope.$apply()sarebbe sufficiente, dal momento che il modello è già stato guardato $. La funzione stop () sta aggiornando il modello (credo che Update sia un riferimento a scope.model), quindi viene chiamato $ apply.
Mark Rajcok,

Sembra che i documenti di AngularJS siano passati da sotto a questa risposta (il primo link non ha "runtime" o $watchsulla pagina e il secondo link è interrotto - almeno adesso). Dolorosamente, le versioni dell'archivio non memorizzavano nella cache qualunque processo asincrono creasse il contenuto.
ruffin

52

AngularJS estende questo ciclo di eventi , creando qualcosa chiamato AngularJS context.

$ Watch ()

Ogni volta che associ qualcosa nell'interfaccia utente, inserisci un $watchin un $watchelenco .

User: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

Qui abbiamo $scope.user, che è legato al primo input, e abbiamo $scope.pass, che è associato al secondo. In questo modo aggiungiamo due $watches alla $watchlista .

Quando viene caricato il nostro modello , AKA nella fase di collegamento, il compilatore cercherà ogni direttiva e creerà tutti gli $watches necessari.

AngularJS fornisce $watch, $watchcollectione $watch(true). Di seguito è riportato un diagramma accurato che spiega in profondità tutti e tre i tratti degli osservatori .

Inserisci qui la descrizione dell'immagine

angular.module('MY_APP', []).controller('MyCtrl', MyCtrl)
function MyCtrl($scope,$timeout) {
  $scope.users = [{"name": "vinoth"},{"name":"yusuf"},{"name":"rajini"}];

  $scope.$watch("users", function() {
    console.log("**** reference checkers $watch ****")
  });

  $scope.$watchCollection("users", function() {
    console.log("**** Collection  checkers $watchCollection ****")
  });

  $scope.$watch("users", function() {
    console.log("**** equality checkers with $watch(true) ****")
  }, true);

  $timeout(function(){
     console.log("Triggers All ")
     $scope.users = [];
     $scope.$digest();

     console.log("Triggers $watchCollection and $watch(true)")
     $scope.users.push({ name: 'Thalaivar'});
     $scope.$digest();

     console.log("Triggers $watch(true)")
     $scope.users[0].name = 'Superstar';
     $scope.$digest();
  });
}

http://jsfiddle.net/2Lyn0Lkb/

$digest ciclo continuo

Quando il browser riceve un evento che può essere gestito dal contesto AngularJS, il $digestloop verrà attivato. Questo loop è composto da due loop più piccoli. Uno elabora la $evalAsynccoda e l'altro elabora il file $watch list. Il $digestciclo attraverso l'elenco di $watchciò che abbiamo

app.controller('MainCtrl', function() {
  $scope.name = "vinoth";

  $scope.changeFoo = function() {
      $scope.name = "Thalaivar";
  }
});

{{ name }}
<button ng-click="changeFoo()">Change the name</button>

Qui ne abbiamo solo uno $watchperché ng-click non crea alcun orologio.

Premiamo il pulsante.

  1. Il browser riceve un evento che entrerà nel contesto di AngularJS
  2. Il $digestciclo verrà eseguito e chiederà modifiche a ogni $ watch.
  3. Poiché quello $watchche stava osservando le modifiche in $ scope.name riporta una modifica, forzerà un altro $digestciclo.
  4. Il nuovo loop non riporta nulla.
  5. Il browser riprende il controllo e aggiorna il DOM in base al nuovo valore di $ scope.name
  6. La cosa importante qui è che OGNI evento che entra nel contesto di AngularJS eseguirà un $digestciclo. Ciò significa che ogni volta che scriviamo una lettera in un input, il ciclo verrà eseguito controllando tutti $watchin questa pagina.

$ Applicare ()

Se chiami $applyquando viene lanciato un evento, questo passerà attraverso il contesto angolare, ma se non lo chiami, verrà eseguito al di fuori di esso. È così facile. $applychiamerà il$digest() loop internamente e ripeterà tutti gli orologi per garantire che il DOM venga aggiornato con il valore appena aggiornato.

Il $apply()metodo attiverà gli osservatori sull'intera $scopecatena mentre il $digest()metodo attiverà solo gli osservatori sulla corrente $scopee sulla sua children. Quando nessuno degli $scopeoggetti di livello superiore deve conoscere le modifiche locali, è possibile utilizzare $digest().


18

Ho trovato molto i video di approfondimento che coprono $watch, $apply, $digeste digerire cicli:

Di seguito sono riportate un paio di diapositive utilizzate in quei video per spiegare i concetti (per ogni evenienza, se i collegamenti sopra riportati vengono rimossi / non funzionanti).

Inserisci qui la descrizione dell'immagine

Nell'immagine sopra, "$ scope.c" non viene guardato in quanto non viene utilizzato in nessuna delle associazioni di dati (nel markup). Gli altri due ( $scope.ae $scope.b) saranno guardati.

Inserisci qui la descrizione dell'immagine

Dall'immagine sopra: in base al rispettivo evento del browser, AngularJS acquisisce l'evento, esegue il ciclo digest (passa attraverso tutti gli orologi per le modifiche), esegue le funzioni dell'orologio e aggiorna il DOM. Se non gli eventi del browser, il ciclo digest può essere attivato manualmente utilizzando $applyo $digest.

Maggiori informazioni su $applye $digest:

Inserisci qui la descrizione dell'immagine


17

Ci sono $watchGroupe $watchCollectionpure. In particolare, $watchGroupè davvero utile se si desidera chiamare una funzione per aggiornare un oggetto che ha più proprietà in una vista che non è un oggetto dom, ad esempio un'altra vista nell'area di disegno canvas, WebGL o server.

Qui, il link della documentazione .


Avrei commentato il $watchCollectionma vedo che l'hai già fatto. Ecco la documentazione al riguardo dal sito AngularJS. Offrono una visione molto bella della $watchprofondità. Nota che le informazioni sono vicine alla fine della pagina.
JabberwockyDecompiler

15

Basta finire di leggere TUTTO quanto sopra, noioso e assonnato (scusate ma è vero). Molto tecnico, approfondito, dettagliato e asciutto. Perché sto scrivendo? Poiché AngularJS è enorme, molti concetti interconnessi possono far impazzire chiunque. Mi chiedevo spesso, non sono abbastanza intelligente da capirli? No! È perché così pochi possono spiegare la tecnologia in un linguaggio for-dummie senza tutte le terminologie! Ok, fammi provare:

1) Sono tutte cose guidate dagli eventi. (Sento la risata, ma continua a leggere)

Se non sai cosa sia guidato dagli eventi Quindi pensa di posizionare un pulsante sulla pagina, collegalo con una funzione usando "on-click", in attesa che gli utenti facciano clic su di esso per attivare le azioni che pianifichi all'interno del funzione. O pensa al "trigger" di SQL Server / Oracle.

2) $ watch è "on-click".

La cosa speciale è che prende 2 funzioni come parametri, il primo dà il valore dall'evento, il secondo prende in considerazione il valore ...

3) $ digest è il capo che controlla instancabilmente , bla-bla-bla ma un buon capo.

4) $ apply ti dà il modo in cui vuoi farlo manualmente , come un sistema a prova di errore (nel caso in cui il clic non si avvii, lo costringi a correre).

Ora rendiamolo visivo. Immagina questo per rendere ancora più facile afferrare l'idea:

In un ristorante,

- I camerieri

dovrebbero prendere ordini dai clienti, questo è

$watch(
  function(){return orders;},
  function(){Kitchen make it;}
);

- MANAGER che corre in giro per assicurarsi che tutti i camerieri siano svegli, rispondenti a qualsiasi segno di cambiamento da parte dei clienti. Questo è$digest()

- PROPRIETARIO ha il massimo potere di guidare tutti su richiesta, questo è$apply()


2
Questo può essere compreso da un bambino di 5 anni. Apprezzo questo tipo di risposta. +1
Chris22,
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.