AngularJS : Prevent error $digest already in progress when calling $scope.$apply()

前端 未结 28 2764
伪装坚强ぢ
伪装坚强ぢ 2020-11-21 22:31

I\'m finding that I need to update my page to my scope manually more and more since building an application in angular.

The only way I know of to do this is to call

相关标签:
28条回答
  • 2020-11-21 23:01

    From a recent discussion with the Angular guys on this very topic: For future-proofing reasons, you should not use $$phase

    When pressed for the "right" way to do it, the answer is currently

    $timeout(function() {
      // anything you want can go here and will safely be run on the next digest.
    })
    

    I recently ran into this when writing angular services to wrap the facebook, google, and twitter APIs which, to varying degrees, have callbacks handed in.

    Here's an example from within a service. (For the sake of brevity, the rest of the service -- that set up variables, injected $timeout etc. -- has been left off.)

    window.gapi.client.load('oauth2', 'v2', function() {
        var request = window.gapi.client.oauth2.userinfo.get();
        request.execute(function(response) {
            // This happens outside of angular land, so wrap it in a timeout 
            // with an implied apply and blammo, we're in action.
            $timeout(function() {
                if(typeof(response['error']) !== 'undefined'){
                    // If the google api sent us an error, reject the promise.
                    deferred.reject(response);
                }else{
                    // Resolve the promise with the whole response if ok.
                    deferred.resolve(response);
                }
            });
        });
    });
    

    Note that the delay argument for $timeout is optional and will default to 0 if left unset ($timeout calls $browser.defer which defaults to 0 if delay isn't set)

    A little non-intuitive, but that's the answer from the guys writing Angular, so it's good enough for me!

    0 讨论(0)
  • 2020-11-21 23:01

    You can use

    $timeout

    to prevent the error.

     $timeout(function () {
                            var scope = angular.element($("#myController")).scope();
                            scope.myMethod();
                            scope.$scope();
                        },1);
    
    0 讨论(0)
  • 2020-11-21 23:02

    The digest cycle is a synchronous call. It won't yield control to the browser's event loop until it is done. There are a few ways to deal with this. The easiest way to deal with this is to use the built in $timeout, and a second way is if you are using underscore or lodash (and you should be), call the following:

    $timeout(function(){
        //any code in here will automatically have an apply run afterwards
    });
    

    or if you have lodash:

    _.defer(function(){$scope.$apply();});
    

    We tried several workarounds, and we hated injecting $rootScope into all of our controllers, directives, and even some factories. So, the $timeout and _.defer have been our favorite so far. These methods successfully tell angular to wait until the next animation loop, which will guarantee that the current scope.$apply is over.

    0 讨论(0)
  • 2020-11-21 23:02

    You can also use evalAsync. It will run sometime after digest has finished!

    scope.evalAsync(function(scope){
        //use the scope...
    });
    
    0 讨论(0)
  • 2020-11-21 23:04

    yearofmoo did a great job at creating a reusable $safeApply function for us :

    https://github.com/yearofmoo/AngularJS-Scope.SafeApply

    Usage :

    //use by itself
    $scope.$safeApply();
    
    //tell it which scope to update
    $scope.$safeApply($scope);
    $scope.$safeApply($anotherScope);
    
    //pass in an update function that gets called when the digest is going on...
    $scope.$safeApply(function() {
    
    });
    
    //pass in both a scope and a function
    $scope.$safeApply($anotherScope,function() {
    
    });
    
    //call it on the rootScope
    $rootScope.$safeApply();
    $rootScope.$safeApply($rootScope);
    $rootScope.$safeApply($scope);
    $rootScope.$safeApply($scope, fn);
    $rootScope.$safeApply(fn);
    
    0 讨论(0)
  • 2020-11-21 23:07

    The shortest form of safe $apply is:

    $timeout(angular.noop)
    
    0 讨论(0)
提交回复
热议问题