What is $$phase in AngularJS?

I found this code snippet which is part of a angular directive someone wrote for bootstrap modal.

//Update the visible value when the dialog is closed                                                                                                                                                                                                            
                //through UI actions (Ok, cancel, etc.)                                                                                                                                                                                                                         
                element.bind("hide.bs.modal", function () {                                                                                                                                                                                                                     
                    scope.modalVisible = false;                                                                                                                                                                                                                                 
                    if (!scope.$$phase && !scope.$root.$$phase)                                                                                                                                                                                                                 
                        scope.$apply();                                                                                                                                                                                                                                         
                });  

I understood that this part is for the latter half of two way binding we bind to hide.bs.modal event and update modal when UI changes.

I just wanted to know why is the person checking $$phase for scope and rootScope before calling apply ?

Can't we straightaway call apply ?

What is $$phase here?

I tried searching a lot, couldn't find any good explanation.

EDIT:

I found where I saw the example: Simple Angular Directive for Bootstrap Modal

Answers


$$phase is a flag set while angular is in a $digest cycle.

Sometimes (in rare cases), you want to check $$phase on the scope before doing an $apply. An error occurs if you try to $apply during a $digest:

Error: $apply already in progress


Davin is totally correct that it's a flag that angular sets during the digest cycle.

But don't use it in your code.

I recently got a chance to ask Misko (angular author) about $$phase, and he said to never use it; it's an internal implementation of the digest cycle, and it's not future safe.

To make sure your code continues to work in the future, he suggested wrapping whatever you want to "safe apply" inside of a $timeout

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

This comes up a lot when you have callbacks or other things that might resolve during the digest cycle (but don't always)

Here's an example snippet from when I was dealing with one of google's libraries: (The rest of the service this was from 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!


In that example, the element binding will get executed from a non-angular event. In most cases, it is safe to just call $apply() without checking the phase.

If you look at the rest of the code, however, there is a $scope function called showModal(). This function calls into the non-angular code which will likely cause a "hide.bs.modal" event to fire. If the event fires via this route, then the call stack is within a $digest.

So, this event does fall within the rare case of a function that will get called from both angular-managed code AND non-angular code. Checking the $$phase in this case is necessary because you don't know how the event originated. If the $$phase is set to something, then the digest loop will finish to completion and $apply() doesn't need to be called.

This pattern is often referred to as "safe apply".


my understanding is it is good to use when digesting or applying the scope. If it is truthy it means that there is currently a $digest or $apply phase in progress. If you are getting related errors you can do $scope.$$phase || $scope.digest(); which will only digest if $scope.$$pahse is falsy.


You can use $scope.$evalAsync() method rather than using the $scope.$apply() externally with the $$phase value check as rightly suggested by @betaorbust:

I recently got a chance to ask Misko (angular author) about $$phase, and he said to never use it; it's an internal implementation of the digest cycle, and it's not future safe.

The good thing about $scope.$evalAsync() is that it gives you what you really want, get the fresh data up with the digest cycle, faster than even the $timeout can:

Up until now, my approach to deferred-$digest-invocation was to replace the $scope.$apply() call with the $timeout() service (which implicitly calls $apply() after a delay). But, yesterday, I discovered the $scope.$evalAsync() method. Both of these accomplish the same thing - they defer expression-evaluation until a later point in time. But, the $scope.$evalAsync() is likely to execute in the same tick of the JavaScript event loop.

Usage is quite simple:

$scope.$evalAsync(
    function() {
       // Call some method here OR 
       $scope.excutemyMethod(); 

       // assign some value;
       $scope.someVariable = "asd"

    }
);

The method inside the $evalAsync() is added to the Async queue so that it can be executed in the following digest cycle by internally triggering $apply which is what we really want.

Also it includes the $timeout call to handle rarest of rare scenarios watching for the async queue length and waiting the tasks in the Async Queue to be executed to put your function in the execution cycle as soon as possible. Refer Ben Nadel blog, which explains this fact.


Need Your Help

Archiving, unarchiving and cleaning objects in cache with Swift

swift nsfilemanager nskeyedarchiver nskeyedunarchiver

I have problems in handling the persistence of some classes in the cache. This is the code I tried and that does not work at all either recovering the archived data or deleting it:

curved road detection in satellite images matlab

matlab image-processing satellite-image

My project is to extract the skeleton of the road in an image using matlab. How to remove the green color shades of trees in the image. I tried hough transform for detecting lines. What could be a ...