Wie verwende ich $ scope. $ Watch und $ scope. $ In AngularJS?

1088

Ich verstehe nicht, wie man $scope.$watchund benutzt $scope.$apply. Die offizielle Dokumentation ist nicht hilfreich.

Was ich nicht speziell verstehe:

  • Sind sie mit dem DOM verbunden?
  • Wie kann ich DOM-Änderungen am Modell aktualisieren?
  • Was ist der Verbindungspunkt zwischen ihnen?

Ich habe dieses Tutorial ausprobiert , aber es erfordert das Verständnis $watchund die Selbstverständlichkeit $apply.

Was tun $applyund $watchtun und wie verwende ich sie angemessen?

ilyo
quelle

Antworten:

1737

Sie müssen wissen, wie AngularJS funktioniert, um es zu verstehen.

Digest-Zyklus und $ scope

AngularJS definiert in erster Linie ein Konzept eines sogenannten Digest-Zyklus . Dieser Zyklus kann als Schleife betrachtet werden, in der AngularJS prüft, ob Änderungen an allen Variablen vorgenommen wurden, die von allen $scopes überwacht werden . Wenn Sie also $scope.myVarin Ihrem Controller definiert haben und diese Variable zum Überwachen markiert wurde , weisen Sie AngularJS implizit an, die Änderungen myVarin jeder Iteration der Schleife zu überwachen .

Eine natürliche Folgefrage wäre: Ist alles damit verbunden $scope, beobachtet zu werden? Zum Glück nein. Wenn Sie auf Änderungen an jedem Objekt in Ihrem Objekt $scopeachten würden, würde die Auswertung einer Digest-Schleife schnell Ewigkeiten dauern und Sie würden schnell auf Leistungsprobleme stoßen. Aus diesem Grund hat uns das AngularJS-Team zwei Möglichkeiten gegeben, eine $scopeVariable als überwacht zu deklarieren (siehe unten).

$ watch hilft dabei, auf $ scope-Änderungen zu achten

Es gibt zwei Möglichkeiten, eine $scopeVariable als überwacht zu deklarieren .

  1. Indem Sie es in Ihrer Vorlage über den Ausdruck verwenden <span>{{myVar}}</span>
  2. Durch manuelles Hinzufügen über den $watchDienst

Anzeige 1) Dies ist das häufigste Szenario, und ich bin sicher, dass Sie es schon einmal gesehen haben, aber Sie wussten nicht, dass dadurch eine Uhr im Hintergrund erstellt wurde. Ja, das hatte es! Durch die Verwendung von AngularJS-Direktiven (z. B. ng-repeat) können auch implizite Uhren erstellt werden.

Anzeige 2) So erstellen Sie Ihre eigenen Uhren . $watchMit dem Dienst können Sie Code ausführen, wenn sich ein an den angehängter Wert $scopegeändert hat. Es wird selten verwendet, ist aber manchmal hilfreich. Wenn Sie beispielsweise bei jeder Änderung von 'myVar' Code ausführen möchten, können Sie Folgendes tun:

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 ermöglicht die Integration von Änderungen in den Digest-Zyklus

Sie können sich die $applyFunktion als einen Integrationsmechanismus vorstellen . Sie sehen, jedes Mal, wenn Sie eine beobachtete Variable$scope ändern , die direkt an das Objekt angehängt ist, weiß AngularJS, dass die Änderung stattgefunden hat. Dies liegt daran, dass AngularJS diese Änderungen bereits überwachen konnte. Wenn dies also in Code geschieht, der vom Framework verwaltet wird, wird der Digest-Zyklus fortgesetzt.

Manchmal möchten Sie jedoch einen Wert außerhalb der AngularJS-Welt ändern und sehen, dass sich die Änderungen normal ausbreiten. Beachten Sie Folgendes: Sie haben einen $scope.myVarWert, der im $.ajax()Handler eines jQuery geändert wird . Dies wird irgendwann in der Zukunft geschehen. AngularJS kann es kaum erwarten, da dies nicht angewiesen wurde, auf jQuery zu warten.

Um dies anzugehen, $applywurde eingeführt. Damit können Sie den Verdauungszyklus explizit starten. Sie sollten dies jedoch nur verwenden, um einige Daten nach AngularJS zu migrieren (Integration mit anderen Frameworks). Verwenden Sie diese Methode jedoch niemals in Kombination mit regulärem AngularJS-Code, da AngularJS dann einen Fehler auslöst.

Wie hängt das alles mit dem DOM zusammen?

Nun, Sie sollten dem Tutorial wirklich noch einmal folgen, jetzt wo Sie das alles wissen. Der Digest-Zyklus stellt sicher, dass die Benutzeroberfläche und der JavaScript-Code synchron bleiben, indem jeder an alle $scopes angehängte Watcher ausgewertet wird, solange sich nichts ändert. Wenn in der Digest-Schleife keine Änderungen mehr vorgenommen werden, gilt dies als abgeschlossen.

Sie können Objekte $scopeentweder explizit im Controller an das Objekt anhängen oder sie {{expression}}direkt in der Ansicht in Form deklarieren .

Ich hoffe, das hilft, einige Grundkenntnisse über all dies zu klären.

Weitere Lesungen:

ŁukaszBachman
quelle
57
"Angular prüft, ob Änderungen an allen Variablen vorgenommen wurden, die an alle $ scopes angehängt sind" - ich denke nicht, dass das ganz richtig ist. Ich glaube, dass Angular nur (schmutzig) $ scope-Eigenschaften überprüft, für die $ watch eingerichtet wurde (beachten Sie, dass die Verwendung von {{}} in einer Ansicht automatisch eine $ watch erstellt). Siehe auch Abschnitt "Überlegungen zur Leistung von Scope $ watch" auf der Seite "Scope" .
Mark Rajcok
5
Das könnte der Fall sein. Ich werde versuchen, etwas Zeit zu finden, um mehr darüber zu lesen und meine Antwort zu bearbeiten.
ŁukaszBachman
15
@ MarkRajcok, du hattest recht. Ich habe meine Antwort geändert und auf einen Artikel hingewiesen, der gut zeigt, wie dies umgesetzt wird.
ŁukaszBachman
3
Was ist damit? ("Control as" -Methode)
Leandro
2
Die Verwendung von "Control as" sollte keinen Einfluss auf die obigen Informationen haben. Mit this.myVar wird myVar in den Bereich aufgenommen.
Marcus Rådell
161

In AngularJS aktualisieren wir unsere Modelle und unsere Ansichten / Vorlagen aktualisieren das DOM "automatisch" (über integrierte oder benutzerdefinierte Anweisungen).

$ apply und $ watch, beide Scope-Methoden, sind nicht mit dem DOM verbunden.

Die Seite " Konzepte " (Abschnitt "Laufzeit") enthält eine ziemlich gute Erklärung der $ Digest-Schleife, $ Apply, der $ evalAsync-Warteschlange und der $ Watch-Liste. Hier ist das Bild, das dem Text beiliegt:

$ Digest-Schleife

Unabhängig davon, welcher Code Zugriff auf einen Bereich hat - normalerweise Controller und Direktiven (ihre Verknüpfungsfunktionen und / oder ihre Controller) - kann ein " watchExpression " eingerichtet werden, den AngularJS anhand dieses Bereichs auswertet. Diese Auswertung erfolgt immer dann, wenn AngularJS in seine $ digest-Schleife eintritt (insbesondere in die "$ watch list" -Schleife). Sie können einzelne Bereichseigenschaften überwachen, eine Funktion definieren, um zwei Eigenschaften zusammen zu überwachen, Sie können die Länge eines Arrays überwachen usw.

Wenn Dinge "in AngularJS" passieren - z. B. geben Sie in ein Textfeld ein, in dem die AngularJS-Zweiwege-Datenbindung aktiviert ist (dh das ng-Modell verwendet), ein $ http-Rückruf ausgelöst wird usw. - $ apply wurde bereits aufgerufen, also haben wir aufgerufen befinden sich innerhalb des Rechtecks ​​"AngularJS" in der obigen Abbildung. Alle watchExpressions werden ausgewertet (möglicherweise mehrmals - bis keine weiteren Änderungen mehr erkannt werden).

Wenn Dinge "außerhalb von AngularJS" passieren - z. B. Sie haben bind () in einer Direktive verwendet und dieses Ereignis dann ausgelöst wird, was dazu führt, dass Ihr Rückruf aufgerufen wird oder ein von jQuery registrierter Rückruf ausgelöst wird - befinden wir uns immer noch im Rechteck "Native". Wenn der Rückrufcode irgendetwas ändert, das eine $ watch beobachtet, rufen Sie $ apply auf, um in das AngularJS-Rechteck zu gelangen, wodurch die $ Digest-Schleife ausgeführt wird. AngularJS bemerkt die Änderung und macht ihre Magie.

Mark Rajcok
quelle
5
Ich verstehe die Idee, was ich nicht verstehe, ist, wie die Daten tatsächlich übertragen werden. Ich habe ein Modell, das ein Objekt mit vielen Daten ist. Ich verwende einige davon, um das DOM zu manipulieren. dann wird einiges davon geändert. Wie platziere ich die geänderten Daten an der richtigen Stelle im Modell? In dem Beispiel, das ich verwendet habe, führt er die Manipulation durch und verwendet am Ende einfach scope.$apply(scope.model). Ich verstehe nicht, welche Daten übertragen werden und wie sie an die richtige Stelle im Modell übertragen werden.
Ilyo
6
Es findet keine magische Datenübertragung statt. Normalerweise sollten Sie bei Angular-Apps die Angular-Modelle ändern, die dann die Ansichts- / DOM-Updates steuern. Wenn Sie das DOM außerhalb von Angular aktualisieren, müssen Sie die Modelle manuell aktualisieren. scope.$apply(scope.model)wird einfach scope.modelals Winkelausdruck ausgewertet und gibt dann eine $ Digest-Schleife ein. In dem Artikel, auf den Sie verweisen, scope.$apply()wäre dies wahrscheinlich ausreichend, da das Modell bereits überwacht wird. Die stop () - Funktion aktualisiert das Modell (ich glaube, toUpdate ist ein Verweis auf scope.model), und dann wird $ apply aufgerufen.
Mark Rajcok
Es sieht so aus, als hätten sich die AngularJS-Dokumente unter dieser Antwort entfernt (der erste Link hat keine "Laufzeit" oder $watchauf der Seite, und der zweite Link ist defekt - jedenfalls ab sofort). Schmerzhafterweise haben die Archivversionen keinen asynchronen Prozess zwischengespeichert, der den Inhalt erstellt hat.
Ruffin
52

AngularJS erweitert diese Ereignisschleife und erstellt so etwas wie AngularJS context.

$ watch ()

Jedes Mal, wenn Sie etwas in der Benutzeroberfläche binden, fügen Sie ein $watchin eine $watchListe ein .

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

Hier haben wir $scope.user, was an die erste Eingabe gebunden ist, und wir haben $scope.pass, was an die zweite Eingabe gebunden ist. Dazu fügen wir $watchder $watchListe zwei es hinzu .

Wenn unsere Vorlage geladen wird, AKA in der Verknüpfungsphase, sucht der Compiler nach jeder Direktive und erstellt alle $watcherforderlichen es.

AngularJS bietet $watch, $watchcollectionund $watch(true). Unten finden Sie ein übersichtliches Diagramm, in dem alle drei von Beobachtern aufgenommenen Elemente ausführlich erläutert werden .

Geben Sie hier die Bildbeschreibung ein

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 Schleife

Wenn der Browser ein Ereignis empfängt, das vom AngularJS-Kontext verwaltet werden kann, wird die $digestSchleife ausgelöst. Diese Schleife besteht aus zwei kleineren Schleifen. Einer verarbeitet die $evalAsyncWarteschlange und der andere die $watch list. Der $digestWille durchläuft die Liste $watch, die wir haben

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

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

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

Hier haben wir nur eine, $watchweil ng-click keine Uhren erstellt.

Wir drücken den Knopf.

  1. Der Browser empfängt ein Ereignis, das in den AngularJS-Kontext eintritt
  2. Die $digestSchleife wird ausgeführt und fragt jede $ watch nach Änderungen.
  3. Da das, $watchwas nach Änderungen in $ scope.name gesucht hat, eine Änderung meldet, wird eine weitere $digestSchleife erzwungen .
  4. Die neue Schleife meldet nichts.
  5. Der Browser erhält das Steuerelement zurück und aktualisiert das DOM, das den neuen Wert von $ scope.name widerspiegelt
  6. Wichtig hierbei ist, dass JEDES Ereignis, das in den AngularJS-Kontext eintritt, eine $digestSchleife ausführt. Das bedeutet, dass jedes Mal, wenn wir einen Buchstaben in eine Eingabe schreiben, die Schleife alle $watchauf dieser Seite überprüft .

$ apply ()

Wenn Sie aufrufen, $applywenn ein Ereignis ausgelöst wird, durchläuft es den Winkelkontext. Wenn Sie es jedoch nicht aufrufen, wird es außerhalb des Ereignisses ausgeführt. So einfach ist das. $applyruft die$digest() Schleife intern auf und durchläuft alle Uhren, um sicherzustellen, dass das DOM mit dem neu aktualisierten Wert aktualisiert wird.

Die $apply()Methode löst Beobachter in der gesamten $scopeKette aus, während die $digest()Methode nur Beobachter in der aktuellen $scopeund ihrer Kette auslöst children. Wenn keines der übergeordneten $scopeObjekte über die lokalen Änderungen Bescheid wissen muss, können Sie verwenden $digest().

Thalaivar
quelle
18

Ich fand sehr eingehenden Videos , die Abdeckung $watch, $apply, $digestund verdauen Zyklen in:

Im Folgenden finden Sie einige Folien, die in diesen Videos verwendet werden, um die Konzepte zu erläutern (nur für den Fall, dass die oben genannten Links entfernt werden / nicht funktionieren).

Geben Sie hier die Bildbeschreibung ein

Im obigen Bild wird "$ scope.c" nicht überwacht, da es in keiner der Datenbindungen (im Markup) verwendet wird. Die anderen beiden ( $scope.aund $scope.b) werden beobachtet.

Geben Sie hier die Bildbeschreibung ein

Aus dem obigen Bild: Basierend auf dem jeweiligen Browserereignis erfasst AngularJS das Ereignis, führt einen Digest-Zyklus durch (durchläuft alle Uhren auf Änderungen), führt Überwachungsfunktionen aus und aktualisiert das DOM. Wenn keine Browserereignisse vorliegen, kann der Digest-Zyklus manuell mit $applyoder ausgelöst werden $digest.

Mehr über $applyund $digest:

Geben Sie hier die Bildbeschreibung ein

user203687
quelle
17

Es gibt $watchGroupund $watchCollectionauch. Dies $watchGroupist insbesondere dann sehr hilfreich, wenn Sie eine Funktion zum Aktualisieren eines Objekts mit mehreren Eigenschaften in einer Ansicht aufrufen möchten, die kein Dom-Objekt ist, z. B. eine andere Ansicht in Canvas, WebGL oder Serveranforderung.

Hier der Link zur Dokumentation .

Utkarsh Bhardwaj
quelle
Ich hätte das kommentiert, $watchCollectionaber ich sehe, dass du es bereits getan hast. Hier finden Sie eine Dokumentation von der AngularJS-Site. Sie bieten ein sehr schönes Bild der $watchTiefe. Beachten Sie, dass sich die Informationen am unteren Rand der Seite befinden.
JabberwockyDecompiler
15

Lesen Sie einfach ALLE oben genannten, langweilig und schläfrig (sorry, aber es ist wahr). Sehr technisch, ausführlich, detailliert und trocken. Warum schreibe ich? Da AngularJS massiv ist, können viele miteinander verbundene Konzepte jeden verrückt machen. Ich habe mich oft gefragt, ob ich nicht klug genug bin, sie zu verstehen. Nein! Das liegt daran, dass so wenige die Technologie in einer For-Dummy-Sprache ohne alle Terminologien erklären können ! Okay, lass mich versuchen:

1) Sie sind alle ereignisgesteuerte Dinge. (Ich höre das Lachen, lese aber weiter)

Wenn Sie nicht wissen, was ereignisgesteuert ist, denken Sie, Sie platzieren eine Schaltfläche auf der Seite, schließen sie mit einer Funktion mit "On-Click" an und warten darauf, dass Benutzer darauf klicken, um die Aktionen auszulösen, die Sie in der Seite ausführen Funktion. Oder denken Sie an "Trigger" von SQL Server / Oracle.

2) $ watch ist "on-click".

Das Besondere daran ist, dass 2 Funktionen als Parameter verwendet werden: Die erste gibt den Wert des Ereignisses an, die zweite berücksichtigt den Wert ...

3) $ Digest ist der Boss, der unermüdlich nachschaut, bla-bla-bla, aber ein guter Boss.

4) $ apply gibt Ihnen die Möglichkeit, wenn Sie es manuell ausführen möchten , wie z. B. ausfallsicher (falls ein Klick nicht aktiviert wird, erzwingen Sie die Ausführung.)

Lassen Sie es uns jetzt visuell machen. Stellen Sie sich das vor, um die Idee noch einfacher zu machen:

In einem Restaurant,

- Kellner

sollen Bestellungen von Kunden entgegennehmen, das ist

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

- MANAGER läuft herum, um sicherzustellen, dass alle Kellner wach sind und auf Anzeichen von Änderungen durch Kunden reagieren. Das ist$digest()

- EIGENTÜMER hat die ultimative Macht, jeden auf Anfrage zu fahren$apply()

Jeb50
quelle
2
Dies kann ein 5-jähriger verstehen. Ich schätze diese Art von Antwort. +1
Chris22