Parfois, j'ai besoin d'utiliser $scope.$apply
dans mon code et parfois cela génère une erreur "Digest déjà en cours". J'ai donc commencé à trouver un moyen de contourner cela et j'ai trouvé cette question: AngularJS: Empêche l'erreur $ digest déjà en cours lors de l'appel de $ scope. $ Apply () . Cependant, dans les commentaires (et sur le wiki angulaire), vous pouvez lire:
Ne faites pas si (! $ Scope. $$ phase) $ scope. $ Apply (), cela signifie que votre $ scope. $ Apply () n'est pas assez haut dans la pile d'appels.
Alors maintenant, j'ai deux questions:
- Pourquoi exactement est-ce un anti-modèle?
- Comment puis-je utiliser $ scope. $ Apply en toute sécurité?
Une autre «solution» pour éviter l'erreur «digest 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 éliminer entièrement la possibilité d'une erreur "digest déjà en cours"?
PS: J'utilise uniquement $ scope. $ Apply dans des callbacks non angularjs qui ne sont pas synchrones. (pour autant que je sache, ce sont des situations dans lesquelles vous devez utiliser $ scope. $ apply si vous voulez que vos modifications soient appliquées)
la source
scope
de l'intérieur angulaire ou de l'extérieur angulaire. Donc, selon cela, vous savez toujours si vous devez appelerscope.$apply
ou non. Et si vous utilisez le même code pour lascope
manipulation angulaire / non angulaire , vous le faites mal, il devrait toujours être séparé ... donc fondamentalement, si vous rencontrez un cas où vous devez vérifierscope.$$phase
, votre code n'est pas conçu de manière correcte, et il y a toujours un moyen de le faire `` de la bonne manière ''digest already in progress
erreurRéponses:
Après quelques recherches supplémentaires, j'ai pu résoudre la question de savoir si son utilisation est toujours sûre
$scope.$apply
. La reponse courte est oui.Longue réponse:
En raison de la façon dont votre navigateur exécute Javascript, il n'est pas possible que deux appels de résumé se heurtent par hasard .
Par conséquent, l'erreur "digest déjà en cours" ne peut se produire que dans une situation: lorsqu'un $ apply est émis dans un autre $ apply, par exemple:
Cette situation ne peut pas se produire si nous utilisons $ scope.apply dans un callback pur non angularjs, comme par exemple le callback de
setTimeout
. Le code suivant est donc à 100% à l'épreuve des balles et il n'est pas nécessaire de faire unif (!$scope.$$phase) $scope.$apply()
même celui-ci est sûr:
Ce qui n'est PAS sûr (car $ timeout - comme tous les helpers angularjs -
$scope.$apply
vous appelle déjà ):Cela explique également pourquoi l'utilisation de
if (!$scope.$$phase) $scope.$apply()
est un anti-pattern. Vous n'en avez tout simplement pas besoin si vous l'utilisez$scope.$apply
de la bonne manière: dans un callback pur js commesetTimeout
par exemple.Lisez http://jimhoskins.com/2012/12/17/angularjs-and-apply.html pour une explication plus détaillée.
la source
$document.bind('keydown', function(e) { $rootScope.$apply(function() { // a passed through function from the controller gets executed here }); });
Je ne sais vraiment pas pourquoi je dois faire $ apply ici, car j'utilise $ document.bind ..function $DocumentProvider(){ this.$get = ['$window', function(window){ return jqLite(window.document); }]; }
Il n'y a pas d'application ici.$timeout
signifie sémantiquement exécuter le code après un délai. Cela pourrait être une chose fonctionnellement sûre à faire, mais c'est un hack. Il devrait y avoir un moyen sûr d'utiliser $ apply lorsque vous ne pouvez pas savoir si un$digest
cycle est en cours ou si vous êtes déjà dans un fichier$apply
.C'est très certainement un anti-pattern maintenant. J'ai vu un résumé exploser même si vous vérifiez la phase $$. Vous n'êtes tout simplement pas censé accéder à l'API interne indiquée par des
$$
préfixes.Tu devrais utiliser
car il s'agit de la méthode préférée dans Angular ^ 1.4 et est spécifiquement exposée en tant qu'API pour la couche d'application.
la source
Dans tous les cas lorsque votre digest en cours et que vous poussez un autre service à digérer, cela donne simplement une erreur ie digest déjà en cours. donc pour remédier à cela, vous avez deux options. vous pouvez vérifier tout autre résumé en cours, comme le sondage.
Premier
si la condition ci-dessus est vraie, vous pouvez appliquer votre $ scope. $ apply otherwies not and
la deuxième solution est d'utiliser $ timeout
il ne laissera pas l'autre condensé démarrer tant que $ timeout n'aura pas terminé son exécution.
la source
$scope.$apply();
.$timeout
c'est la clé! cela fonctionne et plus tard, j'ai trouvé que c'était recommandé aussi.scope.$apply
déclenche un$digest
cycle fondamental pour la liaison de données bidirectionnelleUn
$digest
cycle vérifie les objets, c'est-à-dire les modèles (pour être précis$watch
) attachés pour$scope
évaluer si leurs valeurs ont changé et s'il détecte un changement, il prend les mesures nécessaires pour mettre à jour la vue.Maintenant, lorsque vous utilisez
$scope.$apply
vous faites face à une erreur "Déjà en cours" donc il est assez évident qu'un $ digest est en cours d'exécution mais qu'est-ce qui l'a déclenché?ans -> tous les
$http
appels, tous les ng-clics, répéter, afficher, masquer, etc. déclenchent un$digest
cycle ET LA PIRE PARTIE QUI S'EXÉCUTE DE CHAQUE $ SCOPE.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, vous avez un total de 16 propriétés de portée $ sur votre page.Si vous déclenchez
$scope.$apply
dans le contrôleur D, un$digest
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 donc 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 not$scope.$apply
.Ainsi, un simple ng-click ou ng-show / hide pourrait déclencher un
$digest
cycle sur plus de 100 propriétés, même si l'utilisateur n'a déclenché aucun événement !la source
Utilisez
$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 d'Angular, sans le $ timeout, le seul modèle sera changé mais pas la vue. Parce qu'Angular ne sait pas que cette donnée a été modifiée.
$timeout
dit fondamentalement à Angular d'effectuer le changement lors de la prochaine série de $ digest.J'ai également essayé ce qui suit et cela fonctionne. La différence pour moi est que $ timeout est plus clair.
la source
$http
). Sinon, vous devez répéter ce code partout.$scope.$apply
si vous utilisezsetTimeout
ou$timeout
J'ai trouvé une solution très cool:
injectez là où vous en avez besoin:
la source