fix(popup): synchronously add/remove popups from stack, no matter the animation state

Closes #3131
This commit is contained in:
Andrew
2015-04-16 13:23:43 -06:00
parent 71e89715ee
commit 9baf219e55
2 changed files with 107 additions and 119 deletions

View File

@@ -281,84 +281,79 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $ionicB
buttons: []
}, options || {});
var popupPromise = $ionicTemplateLoader.compile({
template: POPUP_TPL,
scope: options.scope && options.scope.$new(),
appendTo: $ionicBody.get()
});
var contentPromise = options.templateUrl ?
$ionicTemplateLoader.load(options.templateUrl) :
$q.when(options.template || options.content || '');
var self = {};
self.scope = (options.scope || $rootScope).$new();
self.element = jqLite(POPUP_TPL);
self.responseDeferred = $q.defer();
return $q.all([popupPromise, contentPromise]).then(function(results) {
var self = results[0];
var content = results[1];
var responseDeferred = $q.defer();
$ionicBody.get().appendChild(self.element[0]);
$compile(self.element)(self.scope);
self.responseDeferred = responseDeferred;
extend(self.scope, {
title: options.title,
buttons: options.buttons,
subTitle: options.subTitle,
cssClass: options.cssClass,
$buttonTapped: function(button, event) {
var result = (button.onTap || noop)(event);
event = event.originalEvent || event; //jquery events
//Can't ng-bind-html for popup-body because it can be insecure html
//(eg an input in case of prompt)
var body = jqLite(self.element[0].querySelector('.popup-body'));
if (content) {
body.html(content);
$compile(body.contents())(self.scope);
} else {
body.remove();
}
extend(self.scope, {
title: options.title,
buttons: options.buttons,
subTitle: options.subTitle,
cssClass: options.cssClass,
$buttonTapped: function(button, event) {
var result = (button.onTap || noop)(event);
event = event.originalEvent || event; //jquery events
if (!event.defaultPrevented) {
responseDeferred.resolve(result);
}
if (!event.defaultPrevented) {
self.responseDeferred.resolve(result);
}
}
});
$q.when(
options.templateUrl ?
$ionicTemplateLoader.load(options.templateUrl) :
(options.template || options.content || '')
).then(function(template) {
var popupBody = jqLite(self.element[0].querySelector('.popup-body'));
if (template) {
popupBody.html(template);
$compile(popupBody.contents())(self.scope);
} else {
popupBody.remove();
}
});
self.show = function() {
if (self.isShown || self.removed) return;
self.isShown = true;
ionic.requestAnimationFrame(function() {
//if hidden while waiting for raf, don't show
if (!self.isShown) return;
self.element.removeClass('popup-hidden');
self.element.addClass('popup-showing active');
focusInput(self.element);
});
};
self.hide = function(callback) {
callback = callback || noop;
if (!self.isShown) return callback();
self.isShown = false;
self.element.removeClass('active');
self.element.addClass('popup-hidden');
$timeout(callback, 250, false);
};
self.remove = function() {
if (self.removed) return;
self.hide(function() {
self.element.remove();
self.scope.$destroy();
});
self.show = function() {
if (self.isShown || self.removed) return;
self.removed = true;
};
self.isShown = true;
ionic.requestAnimationFrame(function() {
//if hidden while waiting for raf, don't show
if (!self.isShown) return;
self.element.removeClass('popup-hidden');
self.element.addClass('popup-showing active');
focusInput(self.element);
});
};
self.hide = function(callback) {
callback = callback || noop;
if (!self.isShown) return callback();
self.isShown = false;
self.element.removeClass('active');
self.element.addClass('popup-hidden');
$timeout(callback, 250);
};
self.remove = function() {
if (self.removed) return;
self.hide(function() {
self.element.remove();
self.scope.$destroy();
});
self.removed = true;
};
return self;
});
return self;
}
function onHardwareBackButton() {
@@ -367,69 +362,62 @@ function($ionicTemplateLoader, $ionicBackdrop, $q, $timeout, $rootScope, $ionicB
}
function showPopup(options) {
var resultDeferred;
var popupPromise = $ionicPopup._createPopup(options);
var popup = $ionicPopup._createPopup(options);
var showDelay = 0;
if (popupStack.length > 0) {
popupStack[popupStack.length - 1].hide();
resultDeferred = $timeout(doShowPopup, config.stackPushDelay);
showDelay = config.stackPushDelay;
} else {
//Add popup-open & backdrop if this is first popup
$ionicBody.addClass('popup-open');
console.log("RETAIN");
$ionicBackdrop.retain();
//only show the backdrop on the first popup
$ionicPopup._backButtonActionDone = $ionicPlatform.registerBackButtonAction(
onHardwareBackButton,
IONIC_BACK_PRIORITY.popup
);
resultDeferred = doShowPopup();
}
resultDeferred.close = function popupClose(result) {
popupPromise.then(function(popup) {
if (!popup.removed) popup.responseDeferred.resolve(result);
});
// Expose a 'close' method on the returned promise
popup.responseDeferred.promise.close = function popupClose(result) {
if (!popup.removed) popup.responseDeferred.resolve(result);
};
//DEPRECATED: notify the promise with an object with a close method
popup.responseDeferred.notify({ close: popup.responseDeferred.close });
return resultDeferred;
doShow();
function doShowPopup() {
return popupPromise.then(function(popup) {
popupStack.push(popup);
popup.show();
return popup.responseDeferred.promise;
//DEPRECATED: notify the promise with an object with a close method
popup.responseDeferred.notify({
close: resultDeferred.close
});
function doShow() {
popupStack.push(popup);
$timeout(popup.show, showDelay, false);
return popup.responseDeferred.promise.then(function(result) {
var index = popupStack.indexOf(popup);
if (index !== -1) {
popupStack.splice(index, 1);
}
popup.remove();
popup.responseDeferred.promise.then(function(result) {
var index = popupStack.indexOf(popup);
if (index !== -1) {
popupStack.splice(index, 1);
}
if (popupStack.length > 0) {
popupStack[popupStack.length - 1].show();
} else {
$ionicBackdrop.release();
//Remove popup-open & backdrop if this is last popup
$timeout(function() {
// wait to remove this due to a 300ms delay native
// click which would trigging whatever was underneath this
if (!popupStack.length) {
$ionicBody.removeClass('popup-open');
}
}, 400, false);
($ionicPopup._backButtonActionDone || noop)();
}
if (popupStack.length > 0) {
popupStack[popupStack.length - 1].show();
} else {
//Remove popup-open & backdrop if this is last popup
$timeout(function() {
// wait to remove this due to a 300ms delay native
// click which would trigging whatever was underneath this
if (!popupStack.length) {
$ionicBody.removeClass('popup-open');
}
}, 400, false);
($ionicPopup._backButtonActionDone || noop)();
}
return result;
});
popup.remove();
return result;
});
}

View File

@@ -204,7 +204,7 @@ describe('$ionicPopup service', function() {
show: jasmine.createSpy('show'),
responseDeferred: $q.defer()
};
spyOn($ionicPopup, '_createPopup').andReturn($q.when(fakePopup));
spyOn($ionicPopup, '_createPopup').andReturn(fakePopup);
expect($ionicPopup._popupStack.length).toBe(0);
$ionicPopup.show();
expect(fakePopup.show).not.toHaveBeenCalled();
@@ -215,9 +215,9 @@ describe('$ionicPopup service', function() {
}));
it('should have close function which resolves promise with argument', inject(function($ionicPopup, $q, $rootScope) {
var popup = TestUtil.unwrapPromise($ionicPopup._createPopup());
spyOn($ionicPopup, '_createPopup').andReturn($q.when(popup));
var result = $ionicPopup.show();
var popup = TestUtil.unwrapPromise($ionicPopup._createPopup({template: 'foo'}));
spyOn($ionicPopup, '_createPopup').andReturn(popup);
var result = $ionicPopup.show();
spyOn(popup.responseDeferred, 'resolve');
result.close('foobar');
$rootScope.$apply();
@@ -230,7 +230,7 @@ describe('$ionicPopup service', function() {
remove: jasmine.createSpy('remove'),
responseDeferred: $q.defer()
};
spyOn($ionicPopup, '_createPopup').andReturn($q.when(fakePopup));
spyOn($ionicPopup, '_createPopup').andReturn(fakePopup);
var result = $ionicPopup.show();
$timeout.flush();
expect(fakePopup.remove).not.toHaveBeenCalled();
@@ -251,7 +251,7 @@ describe('$ionicPopup service', function() {
show: jasmine.createSpy('show'),
hide: jasmine.createSpy('hide')
};
spyOn($ionicPopup, '_createPopup').andReturn($q.when(fakePopup));
spyOn($ionicPopup, '_createPopup').andReturn(fakePopup);
$ionicPopup._popupStack.unshift(previousPopup);
$ionicPopup.show();
fakePopup.responseDeferred.resolve();
@@ -268,7 +268,7 @@ describe('$ionicPopup service', function() {
var backDoneSpy = jasmine.createSpy('backDone');
spyOn($ionicPlatform, 'registerBackButtonAction').andReturn(backDoneSpy);
spyOn($ionicBackdrop, 'release');
spyOn($ionicPopup, '_createPopup').andReturn($q.when(fakePopup));
spyOn($ionicPopup, '_createPopup').andReturn(fakePopup);
$ionicPopup.show();
fakePopup.responseDeferred.resolve();
$timeout.flush();