Vérification sale dans AngularJS
Nous présenterons comment effectuer une vérification sale dans AngularJs.
Implémenter le Dirty Checking dans AngularJS
AngularJS effectue une vérification sale sur les variables $scope
pour la liaison de données bidirectionnelle. Ember.js
effectue une liaison de données bidirectionnelle en corrigeant par programme les setters et les getters, mais dans Angular, la vérification sale lui permet de rechercher des variables qui pourraient être disponibles ou non.
# AngularJs
$scope.$watch( wExp, listener, objEq );
La fonction $scope.$watch
est utilisée lorsque nous voulons voir quand une variable est modifiée. Nous devons choisir trois arguments pour appliquer cette fonction : wExp
est ce que nous voulons regarder, listener
est ce que nous voulons qu’il fasse quand il est mis à jour, et si nous voulons vérifier une variable ou un objet.
Nous pouvons ignorer cela en utilisant cette fonction lorsque nous voulons vérifier une variable. Passons en revue un exemple comme indiqué ci-dessous.
#AngularJs
$scope.newName = 'Subhan';
$scope.$watch( function( ) {
return $scope.name;
}, function( newVal, oldVal ) {
console.log('$scope.newName is updated!');
} );
Angular enregistrera votre fonction watcher dans le $scope
. Vous pouvez vérifier cela en connectant le $scope
à la console.
Dans $watch
, nous pouvons également utiliser une chaîne à la place d’une fonction. Cela fonctionnera également de la même manière qu’une fonction fonctionne.
Lorsque nous ajoutons une chaîne dans le code source d’Angular, le code sera :
#AngularJs
if (typeof wExp == 'string' && get.constant) {
var newFn = watcher.fn;
watcher.fn = function(newVal, oldVal, scope) {
newFn.call(this, newVal, oldVal, scope);
arrayRemove(array, watcher);
};
}
En appliquant ce code, wExp
sera défini comme une fonction. Cela désignera notre auditeur avec la variable avec notre nom choisi.
la fonction $watchers
dans AngularJS
Tous les watchers sélectionnés seront stockés dans la variable $$watchers
dans $scope
. Vous trouverez un tableau d’objets lorsque nous cochez $$watchers
, comme indiqué ci-dessous.
#AngularJs
$$watchers = [
{
eq: false,
fn: function( newVal, oldVal ) {},
last: 'Subhan',
exp: function(){},
get: function(){}
}
];
La fonction unRegWatch
est renvoyée par la fonction $watch
. Cela montre que si nous voulons attribuer le $scope.$watch
initial à une variable, nous pouvons facilement y parvenir en lui ordonnant d’arrêter de regarder.
Confirmez simplement que nous avons ouvert et examiné le premier $scope
enregistré avant de déconnecter l’observateur.
la fonction $scope.$apply
dans AngularJS
Lorsque nous essayons d’exécuter controller/directive/etc.
, Angular exécute une fonction dans laquelle nous appelons $scope.$watch
. Avant d’administrer la fonction $digest
dans le rootScope
, la fonction $apply
exécutera une fonction que nous avons choisie.
La fonction Angular $apply
est la suivante :
#AngularJs
$apply: function(expr) {
try {
beginPhase('$apply');
return this.$eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
clearPhase();
try {
$rootScope.$digest();
} catch (e) {
$exceptionHandler(e);
throw e;
}
}
}
l’argument expr
dans AngularJS
Lors de l’utilisation de $scope.$apply
, parfois Angular ou nous ferons passer une fonction, l’argument expr
est cette fonction.
Lors de l’utilisation de cette fonction, nous ne trouverons pas le besoin d’utiliser $apply
le plus souvent. Voyons ce qui se passe lorsque ng-keydown
utilise $scope.$apply
.
Pour soumettre la directive, nous utilisons le code suivant.
#AngularJs
var EventNgDirective = {};
forEach(
keydown keyup keypress submit focus blur copy cut paste'.split(' '),
function(newName) {
var newDirectiveName = newDirectiveNormalize('ng-' + newName);
EventNgDirective[newDirectiveName] = ['$parse', function($parse) {
return {
compile: function($element, attr) {
var fn = $parse(attr[directiveName]);
return function ngEventHandler(scope, element) {
element.on(lowercase(newName), function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
};
}
};
}];
}
);
Ce code bouclera sur les différents types d’événements qui peuvent être déclenchés, générant une nouvelle directive appelée ng(eventName)
. Lorsque la fonction de compilation de la directive est concernée, un gestionnaire d’événements est enregistré sur l’élément, où l’événement contient le nom de la directive.
Lorsque cet événement est déclenché, $scope.$apply
sera exécuté par Angular, lui offrant une fonction à exécuter.
C’est ainsi que la valeur $scope
sera mise à jour avec la valeur de l’élément, mais cette liaison n’est qu’une liaison à sens unique. La raison en est que nous avons appliqué ng-keydown
, qui nous permet de changer uniquement lorsque cet événement est appliqué.
En conséquence, une nouvelle valeur est obtenue!
Notre objectif principal est d’atteindre une liaison de données bidirectionnelle. Pour atteindre cet objectif, nous pouvons utiliser le ng-model
.
Pour que ng-model
fonctionne, il utilise à la fois $scope.$watch
et $scope.$apply
.
L’entrée que nous avons donnée rejoindra le gestionnaire d’événements par ng-model
. À ce stade, $scope.$watch
est appelé !
$scope.$watch
est appelé dans le contrôleur de la directive.
#AngularJs
$scope.$watch(function ngModelWatch() {
var value = ngModelGet($scope);
if (ctrl.$modelValue !== value) {
var formatters = ctrl.$formatters,
idx = formatters.length;
ctrl.$modelValue = value;
while(idx--) {
value = formatters[idx](value);
}
if (ctrl.$viewValue !== value) {
ctrl.$viewValue = value;
ctrl.$render();
}
}
return value;
});
Lorsqu’un seul argument est utilisé lors de la définition de $scope.$watch
, la fonction que nous avons choisie sera appliquée même si elle est mise à jour ou non. La fonction présente dans le ng-model
examine si le modèle et la vue sont synchronisés.
S’il n’est pas synchronisé, la fonction donnera au modèle une nouvelle valeur et la mettra à jour. Cette fonction nous permet de savoir quelle est la nouvelle valeur en renvoyant la nouvelle valeur lorsque nous exécutons cette fonction dans $digest
.
Pourquoi l’auditeur n’est pas renvoyé
Revenons au point de l’exemple où nous avons désenrôlé la fonction $scope.$watch
dans la fonction similaire telle que nous l’avons décrite. Nous pouvons maintenant reconnaître la raison pour laquelle nous n’avons pas été informés de la mise à jour de $scope.name
même lorsque nous l’avons mis à jour.
Comme nous le savons, $scope.$apply
est exécuté par Angular sur chaque fonction de contrôleur de directive. La fonction $scope.$apply
exécutera $digest
uniquement à une condition lorsque nous aurons estimé la fonction de contrôleur de la directive.
Cela signifie que nous avons désinscrit la fonction $scope.$watch
avant qu’elle n’ait pu être exécutée, il y a donc peu ou pas de chance qu’elle soit appelée.
la fonction $digest
dans AngularJS
Vous pouvez appeler cette fonction sur le $rootScope
par $scope.$apply
. Nous pouvons exécuter le cycle de digestion sur le $rootScope
, puis il passera sur les portées et exécutera à la place le cycle de digestion.
Le cycle de résumé déclenchera toutes nos fonctions wExp
dans la variable $$watchers
. Il les comparera à la dernière valeur connue.
Si les résultats sont négatifs, l’auditeur sera renvoyé.
Dans le cycle de digestion, lorsqu’il s’exécute, il boucle deux fois. Une fois, il bouclera sur les observateurs, puis bouclera à nouveau jusqu’à ce que le cycle ne soit plus sale
.
Lorsque le wExp
et la dernière valeur connue ne sont pas équivalents, on dit que le cycle est sale. Habituellement, ce cycle s’exécutera une fois et donnera une erreur s’il s’exécute plus de dix fois.
Angular peut exécuter $scope.$apply
sur tout ce qui comporte une possibilité de changement de valeur de modèle.
Vous devez exécuter $scope.$apply();
lorsque nous avons mis à jour $scope
en dehors d’Angular. Cela informera Angular que la portée a été mise à jour.
Concevons une version de base de la vérification sale.
Toutes les données que nous souhaitons sauvegarder seront dans la fonction Scope
. Nous allons développer l’objet sur une fonction pour dupliquer $digest
et $watch
.
Comme nous n’avons besoin d’évaluer aucune fonction par rapport à Scope
, nous n’utiliserons pas $apply
. Nous utiliserons directement $digest
.
Voici à quoi ressemblera notre Scope
:
#AngularJs
var Scope = function( ) {
this.$$watchers = [];
};
Scope.prototype.$watch = function( ) {
};
Scope.prototype.$digest = function( ) {
};
Il y a deux paramètres, wExp
et listener
, que $watch
doit adopter. Nous pouvons définir les valeurs dans Scope
.
Lorsque $watch
est appelé, nous les envoyons dans la valeur $$watcher
précédemment stockée dans Scope
.
#AngularJs
var Scope = function( ) {
this.$$watchers = [];
};
Scope.prototype.$watch = function( wExp, listener ) {
this.$$watchers.push( {
wExp: wExp,
listener: listener || function() {}
} );
};
Scope.prototype.$digest = function( ) {
};
Ici, nous remarquerons que listener
est défini sur une fonction vide.
Vous pouvez facilement enregistrer un $watch
pour toutes les variables lorsqu’aucun écouteur
n’est proposé en suivant cet exemple.
Nous allons maintenant opérer $digest
. Nous pouvons vérifier si l’ancienne valeur et la nouvelle valeur sont équivalentes.
Nous pouvons également renvoyer l’écouteur s’il n’est pas déjà déclenché. Pour y parvenir, nous allons alors boucler jusqu’à ce qu’ils soient équivalents.
La variable dirtyChecking
nous a aidés à atteindre cet objectif, que les valeurs soient égales ou non.
#AngularJs
var Scope = function( ) {
this.$$watchers = [];
};
Scope.prototype.$watch = function( wExp, listener ) {
this.$$watchers.push( {
wExp: wExp,
listener: listener || function() {}
} );
};
Scope.prototype.$digest = function( ) {
var dirtyChecking;
do {
dirtyChecking = false;
for( var i = 0; i < this.$$watchers.length; i++ ) {
var newVal = this.$$watchers[i].wExp(),
oldVal = this.$$watchers[i].last;
if( oldVal !== newVal ) {
this.$$watchers[i].listener(newVal, oldVal);
dirtyChecking = true;
this.$$watchers[i].last = newVal;
}
}
} while(dirtyChecking);
};
Maintenant, nous allons concevoir un nouvel exemple de notre portée. Nous l’attribuerons à $scope
; ensuite, nous enregistrerons une fonction de montre.
Ensuite, nous le mettrons à jour et le digérerons.
#AngularJs
var Scope = function( ) {
this.$$watchers = [];
};
Scope.prototype.$watch = function( wExp, listener ) {
this.$$watchers.push( {
wExp: wExp,
listener: listener || function() {}
} );
};
Scope.prototype.$digest = function( ) {
var dirtyChecking;
do {
dirtyChecking = false;
for( var i = 0; i < this.$$watchers.length; i++ ) {
var newVal = this.$$watchers[i].wExp(),
oldVal = this.$$watchers[i].last;
if( oldVal !== newVal ) {
this.$$watchers[i].listener(newVal, oldVal);
dirtyChecking = true;
this.$$watchers[i].last = newVal;
}
}
} while(dirtyChecking);
};
var $scope = new Scope();
$scope.newName = 'Subhan';
$scope.$watch(function(){
return $scope.newName;
}, function( newVal, oldVal ) {
console.log(newVal, oldVal);
} );
$scope.$digest();
Nous avons implémenté avec succès la vérification sale. Vous observerez ses journaux lorsque nous observerons la console.
#AngularJs
Subhan undefined
Auparavant, $scope.newName
n’était pas défini. Nous l’avons corrigé pour Subhan
.
Nous allons connecter la fonction $digest
à un événement keyup
sur une entrée. En faisant cela, nous n’avons pas à le désigner nous-mêmes.
Cela signifie que nous pouvons également atteindre une liaison de données bidirectionnelle.
#AngularJs
var Scope = function( ) {
this.$$watchers = [];
};
Scope.prototype.$watch = function( wExp, listener ) {
this.$$watchers.push( {
wExp: wExp,
listener: listener || function() {}
} );
};
Scope.prototype.$digest = function( ) {
var dirty;
do {
dirtyChecking = false;
for( var i = 0; i < this.$$watchers.length; i++ ) {
var newVal = this.$$watchers[i].wExp(),
oldVal = this.$$watchers[i].last;
if( oldVal !== newVal ) {
this.$$watchers[i].listener(newVal, oldVal);
dirtyChecking = true;
this.$$watchers[i].last = newVal;
}
}
} while(dirtyChecking);
};
var $scope = new Scope();
$scope.newName = 'Subhan';
var element = document.querySelectorAll('input');
element[0].onkeyup = function() {
$scope.newName = element[0].value;
$scope.$digest();
};
$scope.$watch(function(){
return $scope.newName;
}, function( newVal, oldVal ) {
console.log('Value updated In Input - The new value is ' + newVal);
element[0].value = $scope.newName;
} );
var updateScopeValue = function updateScopeValue( ) {
$scope.name = 'Butt';
$scope.$digest();
};
Avec cette technique, nous pouvons facilement mettre à jour la valeur de l’entrée, comme on le voit dans le $scope.newName
. Vous pouvez également appeler updateScopeValue
, et la valeur de l’entrée le montrera.
Rana is a computer science graduate passionate about helping people to build and diagnose scalable web application problems and problems developers face across the full-stack.
LinkedIn