web-dev-qa-db-fra.com

erreur $ apply déjà en cours

Trace de la pile:

Error: $apply already in progress
at Error (<anonymous>)
at beginPhase (file:///Android_asset/www/built.min.js:7:22740)
at Object.Scope.$apply (file:///Android_asset/www/built.min.js:7:25967)
at navigator.geolocation.getCurrentPosition.that (file:///Android_asset/www/built.min.js:13:8670)
at Object.geolocation.getCurrentPosition (file:///Android_asset/www/plugins/org.Apache.cordova.core.geolocation/www/geolocation.js:122:13)
at Object.getCurrentPosition (file:///Android_asset/www/built.min.js:13:8589)
at Object.getCurrentPosition (file:///Android_asset/www/built.min.js:13:8277)
at Object.getCurrentCity (file:///Android_asset/www/built.min.js:13:8941)
at Object.$scope.locateDevice (file:///Android_asset/www/built.min.js:13:10480)
at file:///Android_asset/www/built.min.js:7:12292:7

fait référence à ce code http://Pastebin.com/B9V6yvF

    getCurrentPosition: cordovaReady(function (onSuccess, onError, options) {

        navigator.geolocation.getCurrentPosition(function () {
            var that = this,
                args = arguments;

            if (onSuccess) {
                $rootScope.$apply(function () {
                    onSuccess.apply(that, args);
                });
            }
        }, function () {
            var that = this,
                args = arguments;
            if (onError) {
                $rootScope.$apply(function () {
                    onError.apply(that, args);
                });
            }
        }, {
            enableHighAccuracy: true,
            timeout: 20000,
            maximumAge: 18000000
        });
    })

Chose étrange, cela fonctionne bien sur mon LG4X, mais sur mon samsung s2, l'erreur ci-dessus est générée. Des idées ce qui ne va pas?

129
Artjom Zabelin

Vous obtenez cette erreur parce que vous appelez $apply dans un cycle de digestion existant.

La grande question est: pourquoi appelez-vous $apply? Vous ne devriez jamais avoir besoin d'appeler $apply à moins que vous n'interfaciez depuis un événement non angulaire. L'existence de $apply signifie généralement que je fais quelque chose de mal (à moins que, encore une fois, le $ apply se produise à partir d'un événement non angulaire).

Si $apply est vraiment approprié ici, envisagez d'utiliser une approche "d'application sans danger":

https://coderwall.com/p/ngisma

102
Brian Genisio

Utilisez simplement $ evalAsync au lieu de $apply.

56
Adrian Neatu

Vous pouvez utiliser cette déclaration:

if ($scope.$root.$$phase != '$apply' && $scope.$root.$$phase != '$digest') {
    $scope.$apply();
}
38
Dariraze

Si la portée doit être appliquée dans certains cas, vous pouvez définir un délai d'attente de sorte que le $ apply soit différé jusqu'au prochain tick

setTimeout(function(){ scope.$apply(); });

ou envelopper votre code dans un $ timeout (function () {..}); car $ appliquera automatiquement la portée à la fin de l’exécution. Si vous avez besoin que votre fonction se comporte de manière synchrone, je ferais la première.

22
jeff.d

Dans angular 1.3, je pense qu'ils ont ajouté une nouvelle fonction - $scope.$applyAsync(). Les appels de fonction s’appliquent plus tard - ils disent au moins 10 ms plus tard. Ce n'est pas parfait, mais au moins cela élimine l'erreur ennuyeuse.

https://docs.angularjs.org/api/ng/type/ $ rootScope.Scope # $ applyAsync

9
user3413723

Dans mon cas, j'utilise $apply avec angular interface de calendrier pour lier un événement:

$scope.eventClick = function(event){           
    $scope.$apply( function() {
        $location.path('/event/' + event.id);
    });
};

Après avoir lu la documentation du problème: https://docs.angularjs.org/error/ $ rootScope/inprog

La partie API Inconsistent (Sync/Async) est très intéressante:

Par exemple, imaginez une bibliothèque tierce qui possède une méthode permettant de récupérer des données pour nous. Dans la mesure où il peut effectuer un appel asynchrone vers un serveur, il accepte une fonction de rappel, qui sera appelée lorsque les données arriveront.

Étant donné que le constructeur MyController est toujours instancié à partir d'un appel $ apply, notre gestionnaire tente de saisir un nouveau bloc $ apply à partir de celui-ci.

Je change le code en:

$scope.eventClick = function(event){           
    $timeout(function() {
        $location.path('/event/' + event.id);
    }, 0);
};

Fonctionne comme un charme!

Ici, nous avons utilisé $ timeout pour planifier les modifications apportées à l'étendue dans une future pile d'appels. En fournissant un délai d'expiration de 0 ms, cela se produira dès que possible et $ timeout garantira que le code sera appelé dans un seul bloc $ apply.

8
mpgn

À tout moment, il ne peut y avoir qu'une seule opération $digest ou $apply en cours. Cela évite très difficilement la détection de bogues dans votre application. La trace de pile de cette erreur vous permet de suivre l'origine de l'appel $apply ou $digest en cours d'exécution, à l'origine de l'erreur.

Plus d'infos: https://docs.angularjs.org/error/$rootScope/inprog?p0=$apply

3
forgottofly

Juste résolu ce problème. Son documenté ici .

J'appelais $rootScope.$apply deux fois dans le même flux. Tout ce que j'ai fait est encapsulé le contenu de la fonction de service avec un setTimeout(func, 1).

2
user405398

Je sais que c'est une vieille question, mais si vous avez vraiment besoin d'utiliser $ scope. $ ApplyAsync ();

1
akaco

J'appelle $ scope. $ S'applique comme ceci aux appels ignorés multiples en une fois.

      var callApplyTimeout = null;
      function callApply(callback) {
          if (!callback) callback = function () { };
          if (callApplyTimeout) $timeout.cancel(callApplyTimeout);

          callApplyTimeout = $timeout(function () {
              callback();
              $scope.$apply();
              var d = new Date();
              var m = d.getMilliseconds();
              console.log('$scope.$apply(); call ' + d.toString() + ' ' + m);
          }, 300);
      }

simplement appeler

callApply();
0
Marosdee Uma