AngularJS
Digest Loop Komplettlösung
Suche…
Syntax
- $ scope. $ watch (watchExpression, Rückruf, [Deep Compare])
- $ scope. $ digest ()
- $ scope. $ apply ([exp])
Zweiwege-Datenbindung
Angular hat etwas Magie unter der Haube. Es ermöglicht das Binden von DOM an reale js-Variablen.
Angular verwendet eine Schleife mit dem Namen " Digest Loop ", die nach jeder Änderung einer Variablen aufgerufen wird - Callbacks, die das DOM aktualisieren.
Zum Beispiel fügt die Direktive ng-model
dieser Eingabe einen keyup
eventListener hinzu :
<input ng-model="variable" />
Jedes Mal, keyup
das keyup
Ereignis keyup
wird, beginnt die Digest-Schleife .
Irgendwann durchläuft die Digest-Schleife einen Rückruf, der den Inhalt dieses Bereichs aktualisiert:
<span>{{variable}}</span>
Der grundlegende Lebenszyklus dieses Beispiels fasst (sehr schematisch) die Wirkungsweise von Winkel zusammen:
- Angular scannt HTML
-
ng-model
Direktiveng-model
erstellt bei der Eingabe einenkeyup
Listener -
expression
innerhalb des Bereichs fügt dem Digest-Zyklus einen Rückruf hinzu
-
- Der Benutzer interagiert mit der Eingabe
-
keyup
listener startet den Digest-Zyklus - Digest-Zyklus ruft den Rückruf auf
- Callback-Aktualisierungen umfassen den Inhalt
-
$ Digest und $ Watch
Die Implementierung der bidirektionalen Datenbindung zum Erreichen des Ergebnisses aus dem vorherigen Beispiel könnte mit zwei Kernfunktionen erfolgen:
- $ digest wird nach einer Benutzerinteraktion aufgerufen (bindendes DOM => variable)
- $ watch setzt einen Callback, der nach Variablenänderungen aufgerufen wird (Bindungsvariable => DOM)
Hinweis: Bei diesem Beispiel handelt es sich um eine Demonstration, nicht um den tatsächlichen Winkelcode
<input id="input"/>
<span id="span"></span>
Die zwei Funktionen, die wir brauchen:
var $watches = [];
function $digest(){
$watches.forEach(function($w){
var val = $w.val();
if($w.prevVal !== val){
$w.callback(val, $w.prevVal);
$w.prevVal = val;
}
})
}
function $watch(val, callback){
$watches.push({val:val, callback:callback, prevVal: val() })
}
Jetzt können wir diese Funktionen verwenden, um eine Variable an das DOM anzuschließen.
var realVar;
//this is usually done by ng-model directive
input1.addEventListener('keyup',function(e){
realVar=e.target.value;
$digest()
}, true);
//this is usually done with {{expressions}} or ng-bind directive
$watch(function(){return realVar},function(val){
span1.innerHTML = val;
});
Natürlich sind die realen Implementierungen komplexer und unterstützen Parameter wie das zu bindende Element und die zu verwendende Variable
Ein laufendes Beispiel finden Sie hier: https://jsfiddle.net/azofxd4j/
der $ scope-Baum
Das vorige Beispiel ist gut genug, wenn wir ein einzelnes HTML-Element an eine einzelne Variable binden müssen.
In der Realität müssen viele Elemente an viele Variablen gebunden werden:
<span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>
Diese ng-repeat
bindet 5 Elemente an 5 Variablen, die als number
, und für jeden von ihnen einen anderen Wert!
Die Art und Weise, wie Winkel dieses Verhalten erreicht, verwendet einen separaten Kontext für jedes Element, das separate Variablen benötigt. Dieser Kontext wird als Bereich bezeichnet.
Jeder Bereich enthält Eigenschaften, dh die an das DOM gebundenen Variablen. Die Funktionen $digest
und $watch
werden als Methoden des Bereichs implementiert.
Das DOM ist eine Baumstruktur, und Variablen müssen in verschiedenen Ebenen der Baumstruktur verwendet werden:
<div>
<input ng-model="person.name" />
<span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
</div>
Wie wir jedoch sahen, unterscheidet sich der Kontext (oder Geltungsbereich) von Variablen in ng-repeat
von dem darüber liegenden Kontext. Um dies zu lösen, werden Winkelbereiche als Baum implementiert.
Jeder Bereich verfügt über ein Array von $digest
Elementen. Wenn Sie seine $digest
Methode aufrufen, werden alle deren $digest
Methoden ausgeführt.
Auf diese Weise wird - nach Änderung der Eingabe - $digest
für den Gültigkeitsbereich der div aufgerufen, der dann den $digest
für seine 5 Kinder ausführt, wodurch der Inhalt aktualisiert wird.
Eine einfache Implementierung für einen Bereich könnte folgendermaßen aussehen:
function $scope(){
this.$children = [];
this.$watches = [];
}
$scope.prototype.$digest = function(){
this.$watches.forEach(function($w){
var val = $w.val();
if($w.prevVal !== val){
$w.callback(val, $w.prevVal);
$w.prevVal = val;
}
});
this.$children.forEach(function(c){
c.$digest();
});
}
$scope.prototype.$watch = function(val, callback){
this.$watches.push({val:val, callback:callback, prevVal: val() })
}
Hinweis: Bei diesem Beispiel handelt es sich um eine Demonstration, nicht um den tatsächlichen Winkelcode