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


Ich verstehe nicht, wie man benutzt $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 es versucht dieses Tutorial, aber es braucht das Verständnis von $watch und $apply selbstverständlich.

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


1007
2018-02-27 12:50


Ursprung


Antworten:


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

Digest-Zyklus und $ Scope

In erster Linie definiert AngularJS ein Konzept eines sog Verdauungszyklus. Dieser Zyklus kann als Schleife betrachtet werden, während der AngularJS prüft, ob Änderungen an allen Variablen vorgenommen wurden schaute bei all dem $scopes. Also wenn du es getan hast $scope.myVar in Ihrem Controller definiert und diese Variable war markiert, um beobachtet zu werden, dann sagst du angularJS implizit, die Änderungen zu überwachen myVar in jeder Iteration der Schleife.

Eine natürliche Folgefrage wäre: Ist alles dran? $scope beobachtet werden? Zum Glück, nein. Wenn Sie nach Änderungen an jedem Objekt in Ihrem suchen würden $scope, dann würde eine Digest-Schleife schnell zu evaluieren sein und Sie würden schnell Leistungsprobleme bekommen. Aus diesem Grund hat uns das AngularJS-Team zwei Möglichkeiten gegeben, einige zu deklarieren $scope Variable als überwacht (siehe unten).

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

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

  1. Indem Sie es in Ihrer Vorlage über den Ausdruck verwenden <span>{{myVar}}</span>
  2. Indem Sie es manuell über die hinzufügen $watch Bedienung

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

Ad 2) So erstellen Sie Ihre eigenen Uhren. $watch Service hilft Ihnen, etwas Code auszuführen, wenn ein Wert an den $scope hat sich verändert. 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 an die denken $apply Funktion als ein Integrationsmechanismus. Sie sehen, jedes Mal wenn Sie etwas ändern beobachtete Variable an die $scope Object direkt, AngularJS wird wissen, dass die Änderung passiert 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 Ändern Sie einen Wert außerhalb der AngularJS-Welt und sehen Sie, wie sich die Änderungen normal ausbreiten. Betrachten Sie das - Sie haben eine $scope.myVar Wert, der innerhalb eines jQuery's geändert wird $.ajax()Handler. 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, $apply wurde vorgestellt. 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 auslöst.

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 an alle angefügte Beobachter ausgewertet wird $scopes solange sich nichts ändert. Wenn in der Digest-Schleife keine weiteren Änderungen mehr auftreten, gilt dies als beendet.

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

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

Weitere Lesungen:


1648
2018-02-27 13:14



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.

Das Konzepte Seite (Abschnitt "Runtime") hat eine ziemlich gute Erklärung der $ Digest-Schleife, $ Apply, der $ evalAsync-Warteschlange und der $ Watch-Liste. Hier ist das Bild, das den Text begleitet:

$digest loop

Egal welcher Code Zugriff auf einen Bereich hat - normalerweise können Controller und Direktiven (ihre Verknüpfungsfunktionen und / oder ihre Controller) einen "watchExpression"dass AngularJS gegen diesen Geltungsbereich auswertet. Diese Auswertung findet immer dann statt, wenn AngularJS in seine $ Digest-Schleife (insbesondere die" $ watch list "-Schleife) eintritt. Sie können einzelne Scope-Eigenschaften betrachten, eine Funktion definieren, um zwei Eigenschaften zusammen zu beobachten. Sie können die Länge eines Arrays usw. beobachten

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 ausgelöst 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 - z. B. haben Sie bind () in einer Direktive verwendet und dann wird dieses Ereignis ausgelöst, was dazu führt, dass Ihr Callback aufgerufen wird oder einige jQuery-registrierte Callback-Brände - wir befinden uns 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.


152
2018-02-28 00:48



Dieser Blog Es wurde alles abgedeckt, was Beispiele und verständliche Erklärungen schafft.

Das AngularJS $scope Funktionen $watch(), $digest() und $apply() sind einige der zentralen Funktionen in AngularJS. Verstehen $watch(), $digest() und $apply() 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 auf der $scope object. Der Rahmen "beobachtet" die Variable. Uhren werden mit dem erstellt $scope.$watch() Funktion, die ich später in diesem Text behandeln werde.

An entscheidenden Punkten Ihrer Anwendung ruft AngularJS die $scope.$digest() Funktion. Diese Funktion 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-Funktion aufgerufen. Die Listener-Funktion führt alle erforderlichen Aufgaben aus, z. B. das Ändern eines HTML-Texts, um den neuen Wert der beobachteten Variablen widerzuspiegeln. Und so kam es dass der $digest() Funktion löst die Aktualisierung der Datenbindung aus.

Meistens ruft AngularJS $ scope. $ Watch () und $scope.$digest() Funktionen für Sie, aber in einigen Situationen müssen Sie sie möglicherweise selbst anrufen. Daher ist es wirklich gut zu wissen, wie sie funktionieren.

Das $scope.$apply() Funktion wird verwendet, um Code auszuführen und dann aufzurufen $scope.$digest() Danach werden alle Uhren überprüft und die entsprechenden Watch-Listener-Funktionen aufgerufen. Das $apply() Funktion ist nützlich, wenn Sie AngularJS mit anderem Code integrieren.

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

$ watch ()

Das $scope.watch() Funktion erstellt eine Uhr einer Variablen. Wenn Sie eine Uhr registrieren, übergeben Sie zwei Funktionen als Parameter an die $watch() Funktion:

  • Eine Wertfunktion
  • Eine Listener-Funktion

Hier ist ein Beispiel:

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

Die erste Funktion ist die Wertfunktion und die zweite Funktion 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 valle gibt den Wert zurück $scope Variable scope.data.myVar. Wenn sich der Wert dieser Variablen ändert, wird ein anderer Wert zurückgegeben, und AngularJS ruft die Listener-Funktion auf.

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

Die Listener-Funktion 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ättest du das mit dem Code machen können {{ data.myVar }, aber dies ist nur ein Beispiel dafür, was Sie innerhalb der Listener-Funktion tun können.

$ verdauen ()

Das $scope.$digest() Funktion durchläuft alle Uhren in der $scope objectund seine untergeordneten $ scope-Objekte (falls vorhanden). Wann $digest() Iteriert über die Uhren, ruft 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-Funktion für diese Uhr aufgerufen.

Das $digest() Die Funktion wird aufgerufen, wenn AngularJS dies für notwendig erachtet. Zum Beispiel, nachdem ein Button-Klick-Handler ausgeführt wurde, oder nach einem AJAX Aufruf kehrt zurück (nachdem die done () / fail () Callback-Funktion ausgeführt wurde).

Sie können auf einige Fälle stoßen, in denen AngularJS nicht anruft $digest() Funktion für Sie. Sie werden dies normalerweise feststellen, wenn Sie feststellen, dass die Datenbindungen die angezeigten Werte nicht aktualisieren. In diesem Fall rufen Sie an $scope.$digest() und es sollte funktionieren. Oder Sie können vielleicht verwenden $scope.$apply() stattdessen was ich im nächsten Abschnitt erklären werde.

$ apply ()

Das $scope.$apply() Funktion nimmt eine Funktion als Parameter, der 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 Funktion wurde an die $apply() Funktion als Parameter ändert den Wert von $scope.data.myVar. Wenn die Funktion AngularJS beendet, wird das aufgerufen $scope.$digest() funktionieren so, dass alle Uhren auf Änderungen der beobachteten Werte überprüft werden.

Beispiel

Um zu veranschaulichen, wie $watch(), $digest() und $apply() funktioniert, schau dir dieses Beispiel an:

<div ng-controller="myController">
    {{data.time}}

    <br/>
    <button ng-click="updateTime()">update time - ng-click</button>
    <button id="updateTimeButton"  >update time</button>
</div>


<script>
    var module       = angular.module("myapp", []);
    var myController1 = module.controller("myController", function($scope) {

        $scope.data = { time : new Date() };

        $scope.updateTime = function() {
            $scope.data.time = new Date();
        }

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

sein Beispiel bindet die $scope.data.time Variable zu einer Interpolationsanweisung, die den Variablenwert in die HTML-Seite zusammenführt. Diese Bindung erstellt intern eine Uhr auf dem $scope.data.time variable.

Das Beispiel enthält außerdem zwei Schaltflächen. Die erste Schaltfläche hat eine ng-click Zuhörer anhängen. Wenn diese Schaltfläche angeklickt wird, wird die $scope.updateTime() Funktion wird aufgerufen, und danach ruft AngularJS auf $scope.$digest() damit Datenbindungen aktualisiert werden.

Die zweite Schaltfläche ruft einen Standard-JavaScript-Ereignis-Listener aus der Controller-Funktion auf. Wenn auf die zweite Schaltfläche geklickt wird, wird die Listener-Funktion ausgeführt. Wie Sie sehen, sind die Listener-Funktionen für beide Schaltflächen fast identisch, aber wenn die Listener-Funktion der zweiten Schaltfläche aufgerufen wird, wird die Datenbindung nicht aktualisiert. Das ist, weil die $scope.$digest() wird nicht aufgerufen, 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. Aktualisiert $scope.data.time Variable, aber die neue Zeit wird nie angezeigt.

Um das zu beheben, können wir ein hinzufügen $scope.$digest() Rufen Sie die letzte Zeile des Ereignis-Listeners für die Schaltfläche wie folgt auf:

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

Anstatt anzurufen $digest() Innerhalb der Tasten - Listener - Funktion hätten Sie auch die $apply() Funktion wie folgt:

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

Beachten Sie, wie die $scope.$apply() Funktion wird von innerhalb der Schaltfläche Ereignis-Listener aufgerufen, und wie das Update der $scope.data.timeVariable wird innerhalb der Funktion ausgeführt, die als Parameter an die übergeben wird $apply() Funktion. Wenn das $apply() Funktionsaufruf beendet AngularJS-Aufrufe $digest() intern, so dass alle Datenbindungen aktualisiert werden.


53
2018-02-22 10:13



AngularJS erweitert dies Ereignisschleifeetwas erschaffen, das genannt wird AngularJS context.

$ watch ()

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

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

Hier haben wir $scope.user, die an den ersten Eingang gebunden ist, und wir haben $scope.pass, die an die zweite gebunden ist. Dazu fügen wir zwei hinzu $watches zu den $watch Liste.

Wenn unsere Vorlage In der Linkphase wird AKA geladen, der Compiler sucht nach allen Anweisungen und erstellt alle $watches ist notwendig.

AngularJS bietet $watch, $watchcollection und $watch(true). Unten ist ein ordentliches Diagramm, das alle drei davon erklärt Beobachter in der Tiefe.

Enter image description here

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 der $digest Schleife wird ausgelöst. Diese Schleife besteht aus zwei kleineren Schleifen. Man verarbeitet das $evalAsync Warteschlange, und der andere verarbeitet die $watch list. Das $digest Durchläuft die Liste von $watch dass 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 einen $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. Das $digest Die Schleife wird ausgeführt und fragt jeden $ watch nach Änderungen ab.
  3. Seit der $watch was auf Änderungen in $ scope.name geachtet hat meldet eine Änderung, es wird eine andere zwingen $digest Schleife.
  4. Die neue Schleife meldet nichts.
  5. Der Browser erhält die Kontrolle zurück und es wird das DOM aktualisieren Reflektiert den neuen Wert von $ scope.name
  6. Die wichtige Sache hier ist, dass JEDES Ereignis, das in den AngularJS-Kontext eintritt, ein ausgeführt wird $digest Schleife. Das bedeutet, dass jedes Mal, wenn wir einen Buchstaben in eine Eingabe schreiben, die Schleife ausgeführt wird und alle überprüft wird $watch auf dieser Seite.

$ apply ()

Wenn du anrufst $apply Wenn ein Ereignis ausgelöst wird, wird es durch den eckigen Kontext gehen, aber wenn du es nicht nennst, wird es außerhalb des Ereignisses laufen. So einfach ist das. $apply werde den anrufen $digest() Schleife intern und es wird über alle Uhren iterieren, um sicherzustellen, dass das DOM mit dem neu aktualisierten Wert aktualisiert wird.

Das $apply() Methode wird Beobachter auf der gesamten auslösen $scope Kette, während die $digest() Methode wird nur Beobachter auf der aktuellen Seite auslösen $scope und sein children. Wenn keiner von denen, die höher liegen $scope Objekte müssen über die lokalen Änderungen Bescheid wissen, die Sie verwenden können $digest().


37
2018-04-22 13:06



Es gibt $watchGroup und $watchCollection auch. Speziell, $watchGroup ist wirklich hilfreich, wenn Sie eine Funktion aufrufen möchten, um ein Objekt mit mehreren Eigenschaften in einer Ansicht zu aktualisieren, die kein dom-Objekt ist, z. andere Ansicht in Canvas, WebGL oder Server Anfrage. Hier die Dokumentation Verknüpfung.


17
2018-03-18 10:50



Ich habe sehr ausführliche Videos gefunden, die abdecken $watch, $apply, $digest und Verdauungszyklen in:

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).

Enter image description here

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

Enter image description here

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 es sich nicht um Browser-Ereignisse handelt, kann der Digest-Zyklus manuell mit ausgelöst werden $apply oder $digest.

Mehr über $apply und $digest:

Enter image description here


15
2017-11-20 16:28



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 einem erklären können for-dummie Sprache ohne alle Terminologien! 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   denke, du platzierst einen Knopf   auf der Seite, hook es w / a-Funktion mit "on-Klick", warten auf   Klicken Sie auf den Benutzer, um die Aktionen auszulösen, die Sie innerhalb der   Funktion. Oder denken Sie an "Trigger" von SQL Server / Oracle.

2) $ watch ist "on-click". 

Das Besondere daran ist, dass es als Parameter zwei Funktionen benötigt   gibt den Wert aus dem Ereignis, zweiter nimmt den Wert in   Berücksichtigung...

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

4) $ apply gibt Ihnen den Weg, wenn Sie es manuell machen wollen, wie eine ausfallsichere Datei (falls On-Click nicht eintritt, erzwingen Sie, dass es ausgeführt wird.)

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

In einem Restaurant, 

- Kellner sollen Bestellungen von Kunden übernehmen, das ist

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

- MANAGER herumlaufen, um sicherzustellen, dass alle Kellner wach sind und auf Anzeichen von Veränderungen seitens der Kunden reagieren. Das ist $digest()

- INHABER hat die ultimative Kraft, um alle auf Anfrage zu fahren, das ist $apply()


10
2018-02-07 01:20