AngularJS
genomgång av slingan
Sök…
Syntax
- $ omfattning. $ klocka (klockuttryck, återuppringning, [djup jämförelse])
- $ Omfattning. $ Smälta ()
- $ Omfattning. $ Gäller ([exp])
tvåvägsdata bindande
Angular har lite magi under huven. det gör det möjligt att binda DOM till verkliga js-variabler.
Angular använder en slinga, kallad " digest loop ", som kallas efter varje ändring av en variabel - samtalsuppringningar som uppdaterar DOM.
Till exempel keyup
ng-model
keyup
en keyup
händelseListener till denna ingång:
<input ng-model="variable" />
Varje gång keyup
bränderna händelse det smälta sling startar.
Vid något tillfälle, det smälta sling itererar över en callback som uppdaterar innehållet i denna spännvidd:
<span>{{variable}}</span>
Den grundläggande livscykeln i detta exempel sammanfattar (mycket schematiskt) hur vinkel fungerar ::
- Vinkelskannar html
-
ng-model
keyup
skapar enkeyup
på input -
expression
inuti spännvidden lägger till ett återuppringning till digerecykeln
-
- Användaren interagerar med input
-
keyup
lyssnaren börjar matningscykeln - digest-cykel ringer återuppringningen
- Uppringningens uppdatering spänner innehåll
-
$ digest och $ watch
Implementering av tvåvägs-databindning, för att uppnå resultatet från föregående exempel, kan göras med två kärnfunktioner:
- $ digest kallas efter en användarinteraktion (bindande DOM => variabel)
- $ watch ställer in en återuppringning som ska kallas efter variabla förändringar (bindningsvariabel => DOM)
Obs! Detta exempel är en demonstration, inte den faktiska vinkelkoden
<input id="input"/>
<span id="span"></span>
De två funktionerna vi behöver:
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() })
}
Nu kan vi nu använda dessa funktioner för att ansluta en variabel till DOM (vinkel kommer med inbyggda direktiv som kommer att göra detta åt dig):
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;
});
Självklart är de verkliga implementeringarna mer komplexa, och stödsparametrar som vilket element du vill binda till och vilken variabel som ska användas
Ett löpande exempel kan hittas här: https://jsfiddle.net/azofxd4j/
$ scope-trädet
Det föregående exemplet är tillräckligt bra när vi måste binda ett enda html-element till en enda variabel.
I verkligheten - vi måste binda många element till många variabler:
<span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>
Denna ng-repeat
binder 5 element till 5 variabler som kallas number
, med ett annat värde för var och en av dem!
Det sätt som vinkeln uppnår detta beteende använder ett separat sammanhang för varje element som behöver separata variabler. Detta sammanhang kallas en omfattning.
Varje omfattning innehåller egenskaper, som är variablerna bundna till DOM, och funktionerna $digest
och $watch
implementeras som metoder för omfattningen.
DOM är ett träd, och variabler måste användas i olika nivåer av trädet:
<div>
<input ng-model="person.name" />
<span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
</div>
Men som vi såg är sammanhanget (eller omfattningen) av variabler i ng-repeat
annorlunda än sammanhanget ovanför. För att lösa detta - vinklar implementerar omfång som ett träd.
Varje räckvidd har en mängd barn, och att kalla sin $digest
metod kommer att köra alla sina barns $digest
metod.
På det här sättet - efter att ha ändrat ingången - kallas $digest
för div: s omfattning, som sedan kör $digest
för sina 5 barn - vilket kommer att uppdatera dess innehåll.
En enkel implementering av en omfattning kan se ut så här:
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() })
}
Obs! Detta exempel är en demonstration, inte den faktiska vinkelkoden