Wie verwende ich $ scope. $ Watch und $ scope. $ Apply in AngularJS?

Ich verstehe nicht, wie $scope.$watch und $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 das Verständnis von $watch und $apply selbstverständlich.

Was tun $apply und $watch , und wie verwende ich sie angemessen?

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

Digest-Zyklus und $ Scope

In erster Linie definiert AngularJS ein Konzept eines sogenannten Digest-Zyklus . Dieser Zyklus kann als Schleife betrachtet werden, während der AngularJS prüft, ob Änderungen an allen von allen $scope s beobachteten Variablen vorgenommen wurden. Wenn Sie $scope.myVar in Ihrem Controller definiert haben und diese Variable als überwacht markiert wurde , dann teilen Sie AngularJS implizit mit, die Änderungen in myVar in jeder Iteration der Schleife zu überwachen.

Eine natürliche Folgefrage wäre: Wird alles, was mit $scope ist, beobachtet? Zum Glück, nein. Wenn Sie nach Änderungen an jedem Objekt in Ihrem $scope Ausschau halten würden, würde eine Digest-Schleife schnell zu evaluieren sein und Sie würden schnell performancesprobleme bekommen. Aus diesem Grund gab uns das AngularJS-Team zwei Möglichkeiten, eine $scope Variable als überwacht zu deklarieren (siehe unten).

$ watch hilft dabei, $ scope changes zu hören

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

  1. Indem Sie es in Ihrer Vorlage über den Ausdruck {{myVar}}
  2. Indem Sie es manuell über den $watch Dienst hinzufügen

Ad 1) Dies ist das häufigste Szenario und ich bin sicher, Sie haben es schon einmal gesehen, aber Sie wussten nicht, dass dies eine Uhr im Hintergrund erstellt hat. Ja, es hatte! Mit AngularJS-Direktiven (wie zB ng-repeat ) können auch implizite Uhren erstellt werden.

Ad 2) So erstellen Sie Ihre eigenen Uhren . $watch service hilft Ihnen, etwas Code auszuführen, wenn sich ein Wert, der an den $scope angehängt ist, geändert hat. Es wird selten verwendet, ist aber manchmal hilfreich. Wenn Sie z. B. bei jeder Änderung von “myVar” einen 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 es, Änderungen mit dem Digest-Zyklus zu integrieren

Sie können sich die $apply function als einen Integrationsmechanismus vorstellen . Jedes Mal, wenn Sie eine beobachtete Variable ändern , die direkt an das Objekt $scope angehängt ist, weiß AngularJS, dass die Änderung eingetreten ist. Dies liegt daran, dass AngularJS bereits wusste, diese Änderungen zu überwachen. Wenn es also in Code passiert, 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. Bedenken Sie $scope.myVar : Sie haben einen $scope.myVar Wert, der innerhalb eines $.ajax() jQuery geändert wird. Dies wird irgendwann in der Zukunft passieren. AngularJS kann nicht darauf warten, dass dies geschieht, da es nicht angewiesen wurde, auf jQuery zu warten.

Um dies anzugehen, wurde $apply eingeführt. Damit können Sie den Aufschlusszyklus 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 normalem AngularJS-Code, da AngularJS dann einen Fehler austriggers.

Wie hängt das alles mit dem DOM zusammen?

Nun, du solltest dem Tutorial wirklich folgen, jetzt, wo du das alles weißt. Der Digest-Zyklus stellt sicher, dass die Benutzeroberfläche und der JavaScript-Code synchronisiert bleiben, indem jeder Watcher ausgewertet wird, der an alle $scope s angehängt ist, solange sich nichts ändert. Wenn in der Digest-Schleife keine weiteren Änderungen mehr auftreten, gilt dies als beendet.

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

Ich hoffe, dass hilft, ein grundlegendes Wissen über all das zu klären.

Weitere Lesungen:

  • Machen Sie Ihre eigenen AngularJS, Teil 1: Scopes und Digest

In AngularJS aktualisieren wir unsere Modelle und unsere Ansichten / Templates aktualisieren das DOM “automatisch” (über eingebaute oder benutzerdefinierte Direktiven).

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

Die Seite Konzepte (Abschnitt “Laufzeit”) hat eine ziemlich gute Erklärung für die $ digest-Schleife, $ apply, die $ evalAsync-Warteschlange und die $ watch-Liste. Hier ist das Bild, das den Text begleitet:

$ Digest-Schleife

Der Code, der Zugriff auf einen Bereich hat – normalerweise Controller und Direktiven (ihre Verknüpfungsfunktionen und / oder ihre Controller) – kann einen ” watchExpression ” einrichten , den AngularJS für diesen Bereich evaluiert. Diese Auswertung findet immer dann statt, wenn AngularJS in seine $ Digest-Schleife eintritt (insbesondere die “$ Watch List” -Schleife). Sie können einzelne Bereichseigenschaften anzeigen, Sie können eine function definieren, um zwei Eigenschaften zusammen zu beobachten, Sie können die Länge eines Arrays beobachten usw.

Wenn Dinge “innerhalb von AngularJS” passieren – zB tippen Sie in ein Textfeld, das AngularJS-Zweiwege-Datenbindung aktiviert hat (dh verwendet ng-Modell), einen $ http-Callback ausgetriggers usw. – $ apply wurde bereits aufgerufen, also wir befinden sich innerhalb des Rechtecks ​​”AngularJS” in der obigen Abbildung. Alle watchExpressions werden ausgewertet (möglicherweise mehrfach – bis keine weiteren Änderungen mehr festgestellt werden).

Wenn Dinge “außerhalb von AngularJS” passieren – zB haben Sie bind () in einer Direktive verwendet und dann dieses Ereignis ausgetriggers, was dazu führt, dass Ihr Callback aufgerufen wird, oder einige jQuery-registrierte Callback-Brände – wir sind immer noch im “Native” Rechteck. Wenn der Callback-Code alles ändert, was eine $ watch beobachtet, rufen Sie $ apply auf, um in das AngularJS-Rechteck zu gelangen, was zur Folge hat, dass die $ Digest-Schleife ausgeführt wird, und AngularJS wird die Änderung bemerken und ihre Magie ausüben.

Dieser Blog wurde mit Beispielen und verständlichen Erklärungen versehen.

Die AngularJS $scope functionen $watch(), $digest() und $apply() sind einige der zentralen functionen in AngularJS. $watch() , $digest() und $apply() zu verstehen ist essentiell um AngularJS zu verstehen.

Wenn Sie eine Datenbindung von irgendwo in Ihrer Ansicht zu einer Variablen im $ scope-Objekt erstellen, erstellt AngularJS intern eine “Überwachung”. Eine Uhr bedeutet, dass AngularJS Änderungen in der Variablen des $scope object überwacht. Der Rahmen “beobachtet” die Variable. Uhren werden mit der function $scope.$watch() , die ich später in diesem Text behandeln werde.

An entscheidenden Punkten Ihrer Anwendung ruft AngularJS die function $scope.$digest() . Diese function durchläuft alle Überwachungsfunktionen und überprüft, ob sich eine der überwachten Variablen geändert hat. Wenn sich eine beobachtete Variable geändert hat, wird eine entsprechende Listener-function aufgerufen. Die Listener-function führt alle erforderlichen Aufgaben aus, z. B. das Ändern eines HTML-Texts, um den neuen Wert der beobachteten Variablen widerzuspiegeln. Daher triggers die function $digest() die Aktualisierung der Datenbindung aus.

Die meiste Zeit wird AngularJS die functionen $ scope. $ Watch () und $scope.$digest() für Sie aufrufen, aber in manchen Situationen müssen Sie sie vielleicht selbst aufrufen. Daher ist es wirklich gut zu wissen, wie sie funktionieren.

Die function $scope.$apply() wird verwendet, um Code auszuführen, und ruft danach $scope.$digest() , so dass alle Uhren überprüft werden und die entsprechenden Watch-Listener-functionen aufgerufen werden. Die function $apply() ist nützlich, wenn Sie AngularJS mit anderem Code integrieren.

Ich werde auf die functionen $watch(), $digest() und $apply() im Rest dieses Textes näher eingehen.

$ watch ()

Die function $scope.watch() erstellt eine $scope.watch() einer Variablen. Wenn Sie eine Uhr registrieren, übergeben Sie zwei functionen als Parameter an die function $watch() :

  • Eine Wertfunktion
  • Eine Listener-function

Hier ist ein Beispiel:

 $scope.$watch(function() {}, function() {} ); 

Die erste function ist die Wertfunktion und die zweite function ist die Listenerfunktion.

Die Wertfunktion sollte den Wert zurückgeben, der überwacht wird. AngularJS kann dann den zurückgegebenen Wert mit dem Wert vergleichen, den die Überwachungsfunktion beim letzten Mal zurückgegeben hat. Auf diese Weise kann AngularJS feststellen, ob sich der Wert geändert hat. Hier ist ein Beispiel:

 $scope.$watch(function(scope) { return scope.data.myVar }, function() {} ); 

Diese Beispielfunktion value gibt die $scope Variable scope.data.myVar . Wenn sich der Wert dieser Variablen ändert, wird ein anderer Wert zurückgegeben, und AngularJS ruft die Listener-function auf.

Beachten Sie, dass die Wertfunktion den Gültigkeitsbereich als Parameter verwendet (ohne $ im Namen). Über diesen Parameter kann die Wertfunktion auf den $scope und seine Variablen zugreifen. Die Wertfunktion kann auch globale Variablen anzeigen, wenn Sie diese benötigen, aber meistens wird eine $scope Variable angezeigt.

Die Listener-function sollte alles tun, was zu tun ist, wenn sich der Wert geändert hat. Vielleicht müssen Sie den Inhalt einer anderen Variablen ändern oder den Inhalt eines HTML-Elements oder etwas festlegen. Hier ist ein Beispiel:

 $scope.$watch(function(scope) { return scope.data.myVar }, function(newValue, oldValue) { document.getElementById("").innerHTML = "" + newValue + ""; } ); 

In diesem Beispiel wird der innere HTML-Code eines HTML-Elements auf den neuen Wert der Variablen gesetzt, der im b-Element eingebettet ist und den Wert fett formatiert. Natürlich hätte man das mit dem Code {{ data.myVar } , aber das ist nur ein Beispiel dafür, was man innerhalb der Listener-function machen kann.

$ verdauen ()

Die function $scope.$digest() durchläuft alle im $scope object uhren und deren untergeordneten $ scope-Objekte (falls vorhanden). Wenn $digest() über die Uhren iteriert, ruft es die Wertfunktion für jede Uhr auf. Wenn der von der Wertfunktion zurückgegebene Wert sich von dem Wert unterscheidet, den er beim letzten Aufruf zurückgegeben hat, wird die Listener-function für diese Uhr aufgerufen.

Die function $digest() wird aufgerufen, wenn AngularJS dies für notwendig erachtet. Zum Beispiel, nachdem ein Button-Click-Handler ausgeführt wurde oder nachdem ein AJAX Aufruf zurückgegeben wurde (nachdem die done () / fail () Callback-function ausgeführt wurde).

Es kann vorkommen, dass AngularJS die function $digest() für Sie aufruft. Sie werden dies normalerweise feststellen, wenn Sie feststellen, dass die Datenbindungen die angezeigten Werte nicht aktualisieren. In diesem Fall rufen Sie $scope.$digest() und es sollte funktionieren. Oder Sie können vielleicht $scope.$apply() verwenden, was ich im nächsten Abschnitt erklären werde.

$ apply ()

Die $scope.$apply() function nimmt eine function als Parameter, die ausgeführt wird, und danach $scope.$digest() wird intern aufgerufen. Dadurch können Sie leichter sicherstellen, dass alle Uhren überprüft werden und somit alle Datenbindungen aktualisiert werden. Hier ist ein $apply() Beispiel:

 $scope.$apply(function() { $scope.data.myVar = "Another value"; }); 

Die an die function $apply() function als Parameter ändert den Wert von $scope.data.myVar . Wenn die function AngularJS beendet, ruft sie die function $scope.$digest() , so dass alle Uhren auf Änderungen der beobachteten Werte überprüft werden.

Beispiel

Um zu zeigen, wie $watch() , $digest( ) und $apply() funktionieren, sehen Sie sich dieses Beispiel an:

 
{{data.time}}

Sein Beispiel bindet die Variable $scope.data.time an eine Interpolationsanweisung, die den Variablenwert in die HTML-Seite zusammenführt. Diese Bindung erstellt intern $scope.data.time variable .

Das Beispiel enthält außerdem zwei Schaltflächen. Die erste Schaltfläche ist mit einem ng-click Listener verknüpft. Wenn diese Schaltfläche angeklickt wird, wird die function $scope.updateTime() aufgerufen, und danach ruft AngularJS $scope.$digest() damit die Datenbindungen aktualisiert werden.

Die zweite Schaltfläche ruft einen Standard-JavaScript-Ereignis-Listener aus der Controller-function auf. Wenn auf die zweite Schaltfläche geklickt wird, wird die Listener-function ausgeführt. Wie Sie sehen, sind die Listener-functionen für beide Schaltflächen fast identisch, aber wenn die Listener-function der zweiten Schaltfläche aufgerufen wird, wird die Datenbindung nicht aktualisiert. Das liegt daran, dass $scope.$digest() nicht aufgerufen wird, nachdem der Ereignis-Listener der zweiten Schaltfläche ausgeführt wurde. Wenn Sie also auf die zweite Schaltfläche klicken, wird die Zeit in der Variable $scope.data.time aktualisiert, aber die neue Zeit wird nie angezeigt.

Um das zu beheben, können wir einen $scope.$digest() Aufruf in die letzte Zeile des Listenereignis-Listeners einfügen:

 document.getElementById("updateTimeButton") .addEventListener('click', function() { console.log("update time clicked"); $scope.data.time = new Date(); $scope.$digest(); }); 

Anstatt $digest() in der Listener-function aufzurufen, hätte man auch die function $apply() verwenden können:

 document.getElementById("updateTimeButton") .addEventListener('click', function() { $scope.$apply(function() { console.log("update time clicked"); $scope.data.time = new Date(); }); }); 

Beachten Sie, wie die function $scope.$apply() aus dem Listenereignis-Listener aufgerufen wird und wie die Aktualisierung der Variablen $scope.data.time innerhalb der function ausgeführt wird, die als Parameter an die function $apply() wird. Wenn der functionsaufruf $apply() beendet wird, ruft AngularJS intern $digest() , so dass alle Datenbindungen aktualisiert werden.

AngularJS erweitert diese Ereignisschleife und erzeugt einen sogenannten AngularJS context .

$ watch ()

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

 User:  Password:  

Hier haben wir $scope.user , der an die erste Eingabe gebunden ist, und wir haben $scope.pass , das an die zweite gebunden ist. Dazu fügen wir zwei $watch es der $watch list hinzu .

Wenn unsere Vorlage in der Verknüpfungsphase AKA geladen wird, sucht der Compiler nach allen statementen und erstellt alle benötigten $watch es.

AngularJS bietet $watch , $watchcollection und $watch(true) . Unten ist ein nettes Diagramm, das all die drei erklärt, die von Beobachtern in der Tiefe genommen wurden .

Geben Sie eine Bildbeschreibung hier 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 $digest Schleife ausgetriggers. Diese Schleife besteht aus zwei kleineren Schleifen. Die eine verarbeitet die $evalAsync Warteschlange und die andere verarbeitet die $watch list . Das $digest wird durch die Liste von $watch , die wir haben, durchlaufen

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

Hier haben wir nur eine $watch weil 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 $digest Schleife wird ausgeführt und fragt jeden $ watch nach Änderungen ab.
  3. Da die $watch die auf Änderungen in $ scope.name achtete, eine Änderung meldet, wird eine weitere $digest Schleife erzwungen.
  4. Die neue Schleife meldet nichts.
  5. Der Browser ruft das Steuerelement zurück und aktualisiert das DOM, das den neuen Wert von $ scope.name enthält
  6. Wichtig dabei ist, dass jedes Ereignis, das in den AngularJS-Kontext eintritt, eine $digest Schleife $digest . Das bedeutet, dass jedes Mal, wenn wir einen Buchstaben in eine Eingabe schreiben, die Schleife jede $watch auf dieser Seite überprüft.

$ apply ()

Wenn Sie $apply aufrufen $apply wenn ein Ereignis ausgetriggers wird, wird es den eckigen Kontext durchlaufen, aber wenn Sie es nicht aufrufen, wird es außerhalb ausgeführt. So einfach ist das. $apply ruft die $digest() Schleife intern auf und es wird über alle Uhren iterieren, um sicherzustellen, dass das DOM mit dem neu aktualisierten Wert aktualisiert wird.

Die $apply() -Methode wird Beobachter für die gesamte $scope Kette auslösen, während die $digest() -Methode nur Beobachter für den aktuellen $scope und seine untergeordneten Elemente austriggers. Wenn keines der höheren $scope über die lokalen Änderungen informiert werden muss, können Sie $digest() .

Es gibt auch $watchGroup und $watchCollection . Insbesondere ist $watchGroup sehr hilfreich, wenn Sie eine function zum Aktualisieren eines Objekts mit mehreren Eigenschaften in einer Ansicht aufrufen möchten, die kein dom-Objekt ist, z. B. für andere Ansichten in Canvas-, WebGL- oder Server-Anfragen. Hier der Dokumentationslink.

Ich habe sehr detaillierte Videos gefunden, die $watch , $apply , $digest und digest cycles abdecken:

  • AngularJS – Watcher verstehen, $ watch, $ watchGroup, $ watchCollection, ng-change

  • AngularJS – Digest-Zyklus verstehen (Digest-Phase oder Digest-process oder Digest-Schleife)

  • AngularJS Tutorial – Verstehen von $ apply und $ digest (in der Tiefe)

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

Geben Sie eine Bildbeschreibung hier ein

Im obigen Bild wird “$ scope.c” nicht beobachtet, da es in keiner der Datenbindungen (in Markup) verwendet wird. Die anderen beiden ( $scope.a und $scope.b ) werden beobachtet.

Geben Sie eine Bildbeschreibung hier ein

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

Mehr über $apply und $digest :

Geben Sie eine Bildbeschreibung hier ein

Lesen Sie einfach alles oben, langweilig und schläfrig (tut mir leid, ist aber wahr). Sehr technisch, detailliert, detailliert und trocken. Warum schreibe ich? Da AngularJS massiv ist, können viele miteinander verbundene Konzepte jeden in den Wahnsinn treiben. Ich habe mich oft gefragt, bin ich nicht schlau genug, sie zu verstehen? Nein! Es ist, weil so wenige die Technologie in einer for-dummie Sprache ohne alle Terminologien erklären können! Okay, lass mich es versuchen:

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

Wenn Sie nicht wissen, was ereignisgesteuert ist Dann denken Sie, dass Sie eine Schaltfläche auf der Seite platzieren, verbinden Sie sie mit einer function mit “on-click” und warten darauf, dass die Benutzer darauf klicken, um die Aktionen auszulösen, die Sie in der function. Oder denken Sie an “Trigger” von SQL Server / Oracle.

2) $ watch ist “on-click”.

Das Besondere daran ist, dass es 2 functionen als Parameter benötigt, die erste gibt den Wert des Ereignisses an, die zweite den Wert.

3) $ Digest ist der Chef, der sich unermüdlich umschaut, bla-bla-bla, aber ein guter Chef.

4) $ apply gibt Ihnen den Weg, wenn Sie es manuell machen möchten , wie eine ausfallsichere (falls On-Click nicht eintritt, zwingen Sie es zum Ausführen.)

Jetzt machen wir es visuell. Stellen Sie sich das vor, um die Idee noch einfacher zu machen:

In einem Restaurant,

– WAITERS sollen Bestellungen von Kunden annehmen, das ist

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

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

– EIGENTÜMER hat die ultimative Macht, um alle auf Anfrage zu fahren, das ist $apply()