Ionic-framework: bug: Multiple popups can't be closed (sort of)

Created on 17 Feb 2015  路  23Comments  路  Source: ionic-team/ionic-framework

Type: bug

Platform: all

The issue only happens when multiple popups are opened in quick succession. In this case, the backdrop div remains visible after the last popup has closed. We've been able to repro the issue on iOS 8 and on the desktop (chrome and IE11).

I've included a short codepen to illustrate the issue.
http://codepen.io/anon/pen/VYXLjv

reply

Most helpful comment

This is how i handle it:

    .factory('SecuredPopups', [
        '$ionicPopup',
        '$q',
        function ($ionicPopup, $q) {

            var firstDeferred = $q.defer();
            firstDeferred.resolve();

            var lastPopupPromise = firstDeferred.promise;

            return {
                'show': function (method, object) {
                    var deferred = $q.defer();

                    lastPopupPromise.then(function () {
                        $ionicPopup[method](object).then(function (res) {
                            deferred.resolve(res);
                        });
                    });

                    lastPopupPromise = deferred.promise;

                    return deferred.promise;
                }
            };
        }
    ])

    // ...

    var alertPopup = SecuredPopups.show('alert', {
     title: 'Don\'t eat that!',
     template: 'It might taste good'
   });

   alertPopup.then(function(res) {
     console.log('Thank you for not eating my delicious ice cream cone');
   });

All 23 comments

Some kind of timing issue. The result is 3 divs with classes: 'popup-showing active'. The popupStack isn't updated quickly enough, so each new modal isn't aware of the previous ones.

This issue is still on 1.0.1, I get multiple popups
$ionicPopup.alert({
title: title,
template: body
});

I am having the same issue here, I am on 1.5.5
.k

This is how i handle it:

    .factory('SecuredPopups', [
        '$ionicPopup',
        '$q',
        function ($ionicPopup, $q) {

            var firstDeferred = $q.defer();
            firstDeferred.resolve();

            var lastPopupPromise = firstDeferred.promise;

            return {
                'show': function (method, object) {
                    var deferred = $q.defer();

                    lastPopupPromise.then(function () {
                        $ionicPopup[method](object).then(function (res) {
                            deferred.resolve(res);
                        });
                    });

                    lastPopupPromise = deferred.promise;

                    return deferred.promise;
                }
            };
        }
    ])

    // ...

    var alertPopup = SecuredPopups.show('alert', {
     title: 'Don\'t eat that!',
     template: 'It might taste good'
   });

   alertPopup.then(function(res) {
     console.log('Thank you for not eating my delicious ice cream cone');
   });

@sharvit Thanks for your solution, it helps alot!

This is a common issue that begs for a default solution.

Thanks @sharvit

EDIT: typo

@sharvit Thanks for the solution. Opening the popup works, but how can I close it and open another?

@Florian9-3 that way you can get the close method with the promise:

.factory('SecuredPopups', [
    '$ionicPopup',
    '$q',
    function ($ionicPopup, $q) {

        var firstDeferred = $q.defer();
        firstDeferred.resolve();

        var lastPopupPromise = firstDeferred.promise;

        // Change this var to true if you want that popups will automaticly close before opening another
        var closeAndOpen = false;

        return {
            'show': function (method, object) {
                var deferred = $q.defer();
                var closeMethod = null;
                deferred.promise.isOpen = false;
                deferred.promise.close = function () {
                    if (deferred.promise.isOpen && angular.isFunction(closeMethod)) {
                        closeMethod();
                    }
                };

                if (closeAndOpen && lastPopupPromise.isOpen) {
                    lastPopupPromise.close();
                }

                lastPopupPromise.then(function () {
                    deferred.promise.isOpen = true;
                    var popupInstance = $ionicPopup[method](object);

                    closeMethod = popupInstance.close;
                    popupInstance.then(function (res) {
                        deferred.promise.isOpen = false;
                        deferred.resolve(res);
                    });
                });

                lastPopupPromise = deferred.promise;

                return deferred.promise;
            }
        };
    }
])

// ...

var alertPopupA = SecuredPopups.show('alert', {
 title: 'AAA',
 template: 'AAA'
});

var alertPopupB = SecuredPopups.show('alert', {
 title: 'BBB',
 template: 'BBB'
});

alertPopupA.then(function(res) {
 console.log('AAA Finished!');
});

alertPopupB.then(function(res) {
 console.log('BBB Finished!');
});

console.log('A is: ' + alertPopupA.isOpen ? 'open' : 'closed');
console.log('B is: ' + alertPopupB.isOpen ? 'open' : 'closed');

alertPopupA.close();

I didn't test it, let me know if its work

@sharvit Works like a charm. Thanks so much.

It seems to me that @sharvit's solution only allows for one extra layer of popups. I wanted a generalized solution allowing for an arbitrary amount of popups, and arrived at one which works as follows:
Open a popup A the usual way. Before the next popup B is opened, A is moved to a stack (and closed in the ionic way, I call it 'soft-close'). When B resolves, A is extracted from the stack and reopened. When finally A resolves, this will be propagated to the factory caller (not on the first 'soft-close').

app.factory 'Popup', ($q, $rootScope, $ionicPopup) ->

  stack: []

  show: (options) ->
    deferred = $q.defer()
    # Soft-close currently open popup. This will not resolve deferred.promise.
    @stack[0].close('soft') if @stack.length
    # This timeout hack is necessary because ionic's popup logic would fail without it
    setTimeout =>
      # Pass data through to $ionicPopup service
      currentPopup = $ionicPopup.show options
      currentPopup.options = options
      currentPopup.deferred = deferred
      # Pass through close method
      deferred.promise.close = ->
        currentPopup.close()
      # Add / remove popup from stack
      @stack.unshift currentPopup
      currentPopup.then @handleCloseEvent(currentPopup)
    , 0

    deferred.promise

  close: ->
    # Close all popups in the stack
    angular.forEach @stack, (popup) -> popup.close()

  handleCloseEvent: (popup) ->
    (data) =>
      unless data == 'soft'
        # Remove popup from stack
        @stack.shift()
        if @stack.length
          # Reopen previously closed popup from stack and update the corresponding stack element
          reopenedPopup = $ionicPopup.show @stack[0].options
          reopenedPopup.options = @stack[0].options
          reopenedPopup.deferred = @stack[0].deferred
          @stack[0] = reopenedPopup
          # Set a handler for the new popup promise
          reopenedPopup.then @handleCloseEvent(reopenedPopup)
        # Propagate close event to the app
        popup.deferred.resolve data

I didn't test this with more than two overlapping popups, but it should work in theory ;)

+1

@ajoslin this issue should be opened again, or at least a workaround should be documented somewhere. Do you know a good place where we can add it ?

@sharvit thanks !!

+1

Hey guys, I get "TypeError: $ionicPopup[method] is not a function" when I try to implement the factory @sharvit posted. Does anybody have a workaround?

Hello! Are you all still having this issue? Thanks!

Hello! As it has been a little while since there was any activity on this issue I will be closing it. Thanks for using ionic!

Hello! Are you all still having this issue? Thanks!

@jgw96 yes of course, we have a "dirty" workaround in place (like everyone I think). Do you need something ? May we help ? Did you ask because something change in a new release ?

am still facing the same issue....can anyone help me to fix this problem..
Tanx in advance:-)

@mushopea
You must specified method:
var alertPopupA = SecuredPopups.show('alert', {
title: 'AAA',
template: 'AAA'
});

but am calling the ionic popup using normal js.
so is how to call ur SecuredPopups outside the angular service.

thanks in advance

Create factory like @sharvit, paste SecuredPopups in your controller definition and create $scope.popup there.

$scope.alertPopupA = function() {
    var popup = SecuredPopups.show('alert', {
        title: 'AAA',
        template: 'AAA',
        scope: $scope
    });
}

Now you can call $scope.alertPopupA(); in controller or ng-click="alertPopupA()" in template.

tanks for ur help :+1:

Was this page helpful?
0 / 5 - 0 ratings

Related issues

daveshirman picture daveshirman  路  3Comments

alexbainbridge picture alexbainbridge  路  3Comments

BilelKrichen picture BilelKrichen  路  3Comments

alan-agius4 picture alan-agius4  路  3Comments

vswarte picture vswarte  路  3Comments