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
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:
Most helpful comment
This is how i handle it: