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




scope new (5)

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?


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: <input type="text" ng-model="user" />
Password: <input type="password" ng-model="pass" />

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

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 ausgelöst. 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 }}
<button ng-click="changeFoo()">Change the name</button>

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 ausgelöst 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 löst Watchers für die gesamte $scope Kette aus, während die $digest() -Methode nur Watchers für den aktuellen $scope und seine untergeordneten Elemente auslöst. 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 Funktion 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.


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 Concepts (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:

Der Code, der Zugriff auf einen Bereich hat - normalerweise Controller und Direktiven (ihre Verknüpfungsfunktionen und / oder ihre Controller) - kann einen " watchExpression " watchExpression , 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 Funktion definieren, um zwei Eigenschaften zusammen zu beobachten, die Länge eines Arrays 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 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 - zB haben Sie bind () in einer Direktive verwendet und dann 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 bemerkt die Änderung und macht ihre Magie.


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 Funktion mit "on-click" und warten darauf, dass Benutzer darauf klicken, um die Aktionen auszulösen, die Sie innerhalb der Seite platzieren Funktion. Oder denken Sie an "Trigger" von SQL Server / Oracle.

2) $ watch ist "on-click".

Das Besondere daran ist, dass es 2 Funktionen 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 dir den Weg, wenn du es manuell machen willst , wie eine ausfallsichere (falls on-click nicht eintritt, zwingst du 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()


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

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

Wenn Sie eine Datenbindung von irgendwo in Ihrer Sicht 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 Funktion $scope.$watch() , die ich später in diesem Text behandeln werde.

An entscheidenden Punkten Ihrer Anwendung ruft AngularJS die Funktion $scope.$digest() . 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. Daher löst die Funktion $digest() die Aktualisierung der Datenbindung aus.

Die meiste Zeit wird AngularJS die Funktionen $ 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 Funktion $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-Funktionen aufgerufen werden. Die Funktion $apply() ist nützlich, wenn Sie AngularJS mit anderem Code integrieren.

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

$ watch ()

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

  • 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 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-Funktion auf.

Beachten Sie, wie 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-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ätte man das mit dem Code {{ data.myVar } , aber das ist nur ein Beispiel dafür, was man innerhalb der Listener-Funktion machen kann.

$ verdauen ()

Die Funktion $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-Funktion für diese Uhr aufgerufen.

Die Funktion $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-Funktion ausgeführt wurde).

Es kann vorkommen, dass AngularJS die Funktion $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 Funktion $scope.$apply() nimmt eine Funktion 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 Funktion $apply() Funktion als Parameter ändert den Wert von $scope.data.myVar . Wenn die Funktion AngularJS beendet, ruft sie die Funktion $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:

<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 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 Funktion $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-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 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-Funktion aufzurufen, hätte man auch die Funktion $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 Funktion $scope.$apply() aus dem Listenereignis-Listener aufgerufen wird und wie die Aktualisierung der Variablen $scope.data.time innerhalb der Funktion ausgeführt wird, die als Parameter an die Funktion $apply() wird. Wenn der Funktionsaufruf $apply() beendet wird, ruft AngularJS intern $digest() , so dass alle Datenbindungen aktualisiert werden.





angularjs-scope