Erläutert die Reihenfolge der ngModel-Pipeline, Parser, Formatierer, viewChangeListeners und $ Watcher

7

Es ist nicht einfach, diese Frage zu formulieren, also werde ich versuchen, mit einem Beispiel zu erklären, was ich wissen möchte:

Betrachten Sie diese einfache angularjs app : PLUNKER

%Vor%

Mit diesem HTML:

%Vor%

Die App:

  • Hat einen Controller namens "mainCtrl", in dem wir eine Bereichsvariable namens "isChecked"
  • definiert haben
  • Es gibt auch eine Anweisung namens "testDirective" mit einem isolierten Bereich und einer Bindungseigenschaft namens "isChecked".
  • Und im HTML-Format instanziieren wir das "testDirective" innerhalb der "mainCtrl" und binden die Eigenschaft "isChecked" des Bereichs "mainCtrl" mit der Eigenschaft "isChecked" des isolierten Bereichs der Direktive.
  • Die Direktive rendert ein Kontrollkästchen mit der Scope-Eigenschaft "isChecked" als Modell.
  • Wenn wir das Kontrollkästchen aktivieren oder deaktivieren, können wir sehen, dass beide Eigenschaften beider Bereiche gleichzeitig aktualisiert werden.

So weit, so gut.

Nehmen wir nun eine kleine Änderung vor: PLUNKER

%Vor%

und das:

%Vor%

Das einzige, was wir getan haben, ist:

  • Definieren Sie eine Funktion im Bereich des Controllers, die window.alert anzeigt, wenn das Attribut 'isChecked' des Bereichs des Controllers aktiviert oder deaktiviert ist. (Ich mache absichtlich einen window.alert , weil ich möchte, dass die Ausführung aufhört)
  • Binde diese Funktion in die Direktive
  • ein
  • Im "ng-change" des Kontrollkästchens der Direktive triggern Sie diese Funktion.

Wenn wir nun das Kontrollkästchen aktivieren oder deaktivieren, erhalten wir eine Warnung, und in dieser Warnung können wir sehen, dass der Geltungsbereich der Richtlinie noch nicht aktualisiert wurde. Ok, also würde man denken, dass ng-change ausgelöst wird, bevor das Modell aktualisiert wird. Auch während der Anzeige der Warnung können wir sehen, dass "isChecked" entsprechend dem im Browser gerenderten Text in beiden Bereichen den gleichen Wert hat. Alles klar, keine große Sache, wenn sich die "ng-change" so verhält, können wir immer $watch setzen und die Funktion dort ausführen ... Aber machen wir noch ein Experiment:

Wie folgt: PLUNKER

%Vor%

Jetzt benutzen wir nur eine Funktion des Bereichs der Direktive, um dasselbe zu tun, was die Funktion des Bereichs des Controllers tat, aber dieses Mal stellt sich heraus, dass das Modell aktualisiert wurde, so scheint es An dieser Stelle wird der Geltungsbereich der Richtlinie aktualisiert, aber der Umfang des Controllers wird nicht aktualisiert ... Seltsam!

Stellen wir sicher, dass das der Fall ist: PLUNKER

%Vor%

Diesmal benutzen wir die Funktion des Bereichs der Direktive, um die gebundene Funktion des Controllers auszulösen, und wir übergeben ein Argument an die Funktion des Controllers mit dem Wert des Bereichs der Direktive. Nun können wir in der Funktion des Controllers bestätigen, was wir bereits im vorherigen Schritt vermutet haben: Der isolierte Bereich wird zuerst aktualisiert, dann wird ng-change ausgelöst, und erst danach werden die Bindungen des Bereichs der Direktive ausgelöst aktualisiert.

Nun, endlich, meine Frage / n:

  • Sollte angularjs nicht alle gebundenen Eigenschaften zur gleichen Zeit aktualisieren, bevor Sie etwas anderes tun?
  • Könnte mir jemand detailliert erklären, was intern passiert, um dieses Verhalten zu rechtfertigen?

Mit anderen Worten: Wenn "ng-change" ausgelöst wurde, bevor das Modell aktualisiert wird, kann ich das verstehen, aber es fällt mir sehr schwer zu verstehen, dass eine Funktion nach dem Aktualisieren des Modells und vor dem Beenden ausgelöst wird Füllen Sie die Änderungen der gebundenen Eigenschaften.

Wenn Sie so weit lesen: Herzlichen Glückwunsch und vielen Dank für Ihre Geduld!

Josep

    
Josep 11.09.2014, 02:33
quelle

1 Antwort

19

Um das Problem zusammenzufassen, muss ngModelController einen Prozess durchlaufen, bevor watches ausgelöst wird. Sie protokollieren die äußere Eigenschaft $scope , bevor ngModelController die Änderung verarbeitet und einen $ digest-Zyklus verursacht hat, der wiederum $watchers ausgelöst hat. Ich würde das model bis zu diesem Punkt nicht berücksichtigen.

Dies ist ein komplexes System. Ich habe diese Demo als Referenz gemacht. Ich empfehle, die return -Werte zu ändern, zu tippen und zu klicken - einfach auf verschiedene Arten damit herumzuspielen und das Protokoll zu überprüfen. Dadurch wird schnell klar, wie alles funktioniert.

Demo (viel Spaß!)

ngModelController verfügt über eigene Funktionsarrays, die als Antwort auf verschiedene Änderungen ausgeführt werden können.

ngModelController hat zwei Arten von "Pipelines", um zu bestimmen, was mit einer Art von Änderung zu tun ist. Diese erlauben dem Entwickler, den Fluss von Werten zu steuern.

Wenn sich die als ngModel zugewiesene Bereichseigenschaft ändert, wird die Pipeline $formatter ausgeführt. Diese Pipeline wird verwendet, um zu bestimmen, wie der Wert von $scope in der Ansicht angezeigt werden soll, lässt das Modell jedoch unverändert. Also, ng-model="foo" und $scope.foo = '123' , würden normalerweise 123 in der Eingabe anzeigen, aber der Formatierer könnte 1-2-3 oder einen beliebigen Wert zurückgeben. $scope.foo ist immer noch 123, aber es wird angezeigt, was auch immer der Formatierer zurückgegeben hat.

$parsers behandelt dasselbe, aber umgekehrt. Wenn der Benutzer etwas eingibt, wird die $ -Parser-Pipeline ausgeführt. Was auch immer $parser zurückgibt, wird auf ngModel.$modelValue gesetzt. Wenn der Benutzer also abc und $parser a-b-c zurückgibt, ändert sich die Ansicht nicht, aber $scope.foo ist jetzt a-b-c .

Nachdem entweder $formatter oder $parser ausgeführt wurde, wird $validators ausgeführt. Die Gültigkeit des für den Validator verwendeten Eigenschaftsnamens wird durch den Rückgabewert der Validierungsfunktion ( true oder false ) festgelegt.

$viewChangeListeners werden nach Ansichtsänderungen ausgelöst, nicht nach Modelländerungen. Dies ist besonders verwirrend, weil wir uns auf $scope.foo und NOT ngModel.$modelValue beziehen. Eine Ansicht aktualisiert unweigerlich ngModel.$modelValue (sofern nicht in der Pipeline verhindert), aber das ist nicht das model change , auf das wir uns beziehen. Grundsätzlich werden $viewChangeListeners nach $parsers und NICHT nach $formatters ausgelöst. Wenn sich der Ansichtswert ändert (Benutzertypen), wird $parsers, $validators, then $viewChangeListeners angezeigt. Spaß mal = D

All dies geschieht intern von ngModelController . Während des Vorgangs wird das ngModel -Objekt nicht wie erwartet aktualisiert. Die Pipeline gibt Werte weiter, die sich auf dieses Objekt auswirken. Am Ende des Prozesses wird das Objekt ngModel mit den richtigen $viewValue und $modelValue aktualisiert.

Schließlich wird die ngModelController ausgeführt und ein $digest Zyklus wird ausgeführt, damit der Rest der Anwendung auf die resultierenden Änderungen reagieren kann.

Hier ist der Code aus der Demo, falls etwas damit passieren sollte:

%Vor%

JS:

%Vor%     
m59 11.09.2014, 19:55
quelle