Angularjs $window.open popups being blocked

Problem

I am trying to use the Github API's web auth flow from within an AngularJS app. When my signup form submits, I want to open a new window to send them to the the auth page. Normally I would just use window.open inside a user event to ensure it wouldn't get caught by a popup blocker.

In my angular app, I am wrapping a bit of the Github api in and Angular service, and the code to open the window goes in there. Because of that it gets blocked. I also tried putting it in a function in the controller that gets called by a form via ng-submit.

So the question is, is there an elegant way to open a new page on a form submit from somewhere inside my service or controller, or will I need to find another way to do it?

Problem courtesy of: giodamelio

Solution

You can't get rid of the popup blocker for scripted automated window.open. Only real user's call to action events will open a new window without being blocked by popup blocker. Imagine a situation in a site where there's no popup blocker in browser and javascript opens 100 popups in a loop. Would you like it ? It used to be there in our old good times like a virus but modern browsers are much smart and this annoyance is handled gracefully.

Solution courtesy of: Mahbub

Discussion

You could quite simply create a directive to do this from within a click event content:

yourapp.directive('awesomeClick', ['$parse',function ($parse): ng.IDirective {
    return {
        restrict: 'A',        
        link: (scope, element:JQuery, attrs) => {
            var fn = $parse(attrs.awesomeClick);
            element.on('click', function (event) {

                // open the window if you want here 

                scope.$apply(function () {
                    fn(scope, { $event: event });
                });
            });
        }
    }
}]);
Discussion courtesy of: basarat

Chrome by default blocks popups that are not a result of user's direct action. In your case, I assume, the call to window.open is made inside a callback function. This makes it asynchronous and Chrome blocks it.

//before
$scope.userClicked = function(){
  $http.post('your/api/endpoint',{data:x}).then(function(r){
    $window.open(r.data.url,'window name','width=800,height=600,menubar=0,toolbar=0');
  });
}

However, there is a workaround to make this work. Instantiate your window outside the callback function and you can reference the variable to change the target url of that window in callback function.

//after
$scope.userClicked = function(){
  var popup = $window.open('','window name','width=800,height=600,menubar=0,toolbar=0');
  popup.document.write('loading ...');

  $http.post('your/api/endpoint',{data:x}).then(function(r){
    popup.location.href = r.data.url;
  });
}
Discussion courtesy of: Nirav Gandhi

This recipe can be found in it's original form on Stack Over Flow.