Parfois, j'ai besoin d'utiliser $scope.$apply
dans mon code et génère parfois une erreur "digérer déjà en cours". J'ai donc commencé à trouver un moyen de contourner ce problème et à trouver la question suivante: AngularJS: Empêcher l'erreur $ digest déjà en cours lors de l'appel de $ scope. $ Apply () . Cependant, dans les commentaires (et sur le angular), vous pouvez lire:
Ne le faites pas si (! $ Scope. $$ phase) $ scope. $ Apply (), cela signifie que votre $ scope. $ Apply () n'est pas assez élevé dans la pile d'appels.
Alors maintenant, j'ai deux questions:
Une autre "solution" pour éviter l'erreur "digérer déjà en cours" semble utiliser $ timeout:
$timeout(function() {
//...
});
Est-ce la voie à suivre? Est-ce plus sûr? Voici donc la vraie question: comment puis-je entièrement éliminer la possibilité d’une erreur "digérer déjà en cours"?
PS: Je n’utilise que $ scope. $ S’applique aux callbacks non angularjs qui ne sont pas synchrones. (autant que je sache, il s'agit de situations dans lesquelles vous devez utiliser $ scope. $ s'applique si vous souhaitez que vos modifications soient appliquées)
Après avoir creusé un peu plus, j'ai pu résoudre la question de savoir s'il était toujours prudent d'utiliser $scope.$apply
. La reponse courte est oui.
Longue réponse:
En raison de la manière dont votre navigateur exécute Javascript, il n'est pas possible que deux appels digest soient en collision par hasard .
Le code JavaScript que nous écrivons ne fonctionne pas tous en une fois, mais s’exécute à tour de rôle. Chacun de ces tours tourne sans interruption du début à la fin, et lorsqu'un tour est lancé, rien d'autre ne se passe dans notre navigateur. (de http://jimhoskins.com/2012/12/17/angularjs-and-apply.html )
Par conséquent, l'erreur "digérer déjà en cours" ne peut se produire que dans un cas: lorsqu'un $ apply est émis dans un autre $ apply, par exemple:
$scope.apply(function() {
// some code...
$scope.apply(function() { ... });
});
Cette situation peut ne pas se produire si on utilise $ scope.apply dans un Rappel pur non angularjs, comme par exemple le rappel de setTimeout
. Donc, le code suivant est 100% à l'épreuve des balles et il n'y a aucun besoin de faire un if (!$scope.$$phase) $scope.$apply()
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
même celui-ci est sûr:
$scope.$apply(function () {
setTimeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
});
Qu'est-ce qui est [~ # ~] pas [~ # ~] sûr (parce que $ timeout - comme tous les helpers angularjs - appelle déjà $scope.$apply
pour vous):
$timeout(function () {
$scope.$apply(function () {
$scope.message = "Timeout called!";
});
}, 2000);
Cela explique également pourquoi l'utilisation de if (!$scope.$$phase) $scope.$apply()
est un anti-motif. Vous n'en avez simplement pas besoin si vous utilisez $scope.$apply
Correctement: Dans un callback pur en js comme setTimeout
par exemple.
Lisez http://jimhoskins.com/2012/12/17/angularjs-and-apply.html pour une explication plus détaillée.
C'est très certainement un anti-modèle maintenant. J'ai vu un résumé exploser même si vous vérifiez la phase $$. Vous n'êtes simplement pas censé accéder à l'API interne dénotée par $$
préfixes.
Tu devrais utiliser
$scope.$evalAsync();
car c'est la méthode préférée dans Angular ^ 1.4 et est spécifiquement exposé en tant qu'API pour la couche d'application.
Dans tous les cas, lorsque votre résumé est en cours et que vous poussez un autre service à digérer, cela donne simplement une erreur, c’est-à-dire que le résumé est déjà en cours. donc pour remédier à cela, vous avez deux options. vous pouvez vérifier si un autre résumé est en cours, comme une interrogation.
le premier
if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
$scope.$apply();
}
si la condition ci-dessus est vraie, vous pouvez appliquer votre $ scope. $ ne s'applique pas aux autres méthodes et
deuxième solution utilise $ timeout
$timeout(function() {
//...
})
il ne laissera pas l’autre digérer commencer jusqu’à ce que $ timeout ait terminé son exécution.
scope.$apply
déclenche un $digest
cycle qui est fondamental pour la liaison de données bidirectionnelle
UNE $digest
le cycle vérifie les objets, c’est-à-dire les modèles (pour être précis, $watch
) attaché à $scope
pour évaluer si leurs valeurs ont changé et si une détection est détectée, les étapes nécessaires à la mise à jour de la vue sont alors nécessaires.
Maintenant, quand vous utilisez $scope.$apply
vous faites face à une erreur "Déjà en cours" il est donc évident qu'un $ digest est en cours d'exécution, mais qu'est-ce qui l'a déclenché?
ans -> chaque $http
appels, tout ng-clic, répéter, montrer, masquer etc. déclenchent un $digest
_ cycle ET LA PIRE PIÈCE CELLE DE CHAQUE $ ÉTENDUE.
c'est-à-dire que votre page a 4 contrôleurs ou directives A, B, C, D
Si vous avez 4 $scope
propriétés dans chacune d’elles alors vous avez un total de 16 propriétés $ scope sur votre page.
Si vous déclenchez $scope.$apply
dans le contrôleur D puis un $digest
le cycle vérifiera les 16 valeurs !!! plus toutes les propriétés $ rootScope.
Réponse ->mais $scope.$digest
déclenche un $digest
sur enfant et même portée, il ne vérifie que 4 propriétés. Donc, si vous êtes sûr que les changements dans D n’affecteront pas A, B, C, utilisez $scope.$diges
t pas $scope.$apply
.
Donc, un simple clic-ng ou ng-show/hide pourrait déclencher un $digest
cycle sur plus de 100 propriétés même lorsque l'utilisateur a aucun événement déclenché!
Utilisation $timeout
, c'est la voie recommandée.
Mon scénario est que je dois modifier les éléments de la page en fonction des données que j'ai reçues d'un WebSocket. Et comme il est en dehors de Angular, sans le timeout $, le seul modèle sera modifié, mais pas la vue. Parce que Angular ne sait pas que cette donnée a été modifiée. $timeout
dit fondamentalement Angular) pour effectuer le changement lors du prochain cycle de $ digest.
J'ai aussi essayé la suite et ça marche. La différence pour moi est que $ timeout est plus clair.
setTimeout(function(){
$scope.$apply(function(){
// changes
});
},0)