AngularJS
digestion en boucle
Recherche…
Syntaxe
- $ scope. $ watch (watchExpression, callback, [comparaison profonde])
- $ scope. $ digest ()
- $ scope. $ apply ([exp])
liaison de données bidirectionnelle
Angular a de la magie sous son capot. il permet de lier les variables DOM aux vraies js.
Angular utilise une boucle, appelée " boucle de digestion ", appelée après toute modification d'une variable - appelant des rappels qui mettent à jour le DOM.
Par exemple, la directive ng-model
associe un keyup
eventListener à cette entrée:
<input ng-model="variable" />
Chaque fois que l'événement d' keyup
déclenche, la boucle de résumé commence.
À un moment donné, la boucle de résumé effectue une itération sur un rappel qui met à jour le contenu de cette plage:
<span>{{variable}}</span>
Le cycle de vie de base de cet exemple résume (très schématiquement) le fonctionnement angulaire ::
- Analyses angulaires html
-
ng-model
directiveng-model
crée un écouteurkeyup
en entrée -
expression
intérieur de l'intervalle ajoute un rappel au cycle de digestion
-
- L'utilisateur interagit avec l'entrée
-
keyup
listener commence le cycle de digestion - cycle de digestion appelle le rappel
- Rappel des mises à jour
-
$ digest et $ watch
L'implémentation de la liaison de données à deux voies, pour obtenir le résultat de l'exemple précédent, pourrait être réalisée avec deux fonctions principales:
- $ digest est appelé après une interaction utilisateur (liaison DOM => variable)
- $ watch définit un rappel à appeler après les changements de variables (variable de liaison => DOM)
note: cet exemple est une démonstration, pas le code angulaire réel
<input id="input"/>
<span id="span"></span>
Les deux fonctions dont nous avons besoin:
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() })
}
Maintenant, nous pourrions maintenant utiliser ces fonctions pour connecter une variable au DOM (angular est livré avec des directives intégrées qui le feront pour vous):
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;
});
Bien entendu, les implémentations réelles sont plus complexes et prennent en charge des paramètres tels que l' élément à lier et la variable à utiliser.
Un exemple courant peut être trouvé ici: https://jsfiddle.net/azofxd4j/
l'arbre $ scope
L'exemple précédent est suffisant lorsque nous devons lier un seul élément HTML, à une seule variable.
En réalité, nous avons besoin de lier plusieurs éléments à plusieurs variables:
<span ng-repeat="number in [1,2,3,4,5]">{{number}}</span>
Ce ng-repeat
lie 5 éléments à 5 variables appelées number
, avec une valeur différente pour chacun d'eux!
La façon dont angulaire atteint ce comportement utilise un contexte distinct pour chaque élément nécessitant des variables distinctes. Ce contexte est appelé une portée.
Chaque portée contient des propriétés, qui sont les variables liées au DOM, et les fonctions $digest
et $watch
sont implémentées en tant que méthodes de la portée.
Le DOM est un arbre, et les variables doivent être utilisées à différents niveaux de l'arborescence:
<div>
<input ng-model="person.name" />
<span ng-repeat="number in [1,2,3,4,5]">{{number}} {{person.name}}</span>
</div>
Mais comme nous l'avons vu, le contexte (ou la portée) des variables à l'intérieur de ng-repeat
est différent du contexte au-dessus. Pour résoudre ce problème - angulaire implémente des étendues comme un arbre.
Chaque champ a un tableau d'enfants, et appelle sa $digest
méthode fonctionnera tous ses enfants de $digest
la méthode.
De cette façon - après avoir modifié l'entrée - $digest
est appelé pour la portée du div, qui exécute alors le $digest
pour ses 5 enfants - qui mettra à jour son contenu.
Une implémentation simple pour une portée pourrait ressembler à ceci:
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() })
}
note: cet exemple est une démonstration, pas le code angulaire réel