diff --git a/src/modal/modal.js b/src/modal/modal.js index adcc95fe65..5870ab34c1 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -1,4 +1,4 @@ -angular.module('ui.bootstrap.modal', []) +angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition']) /** * A helper, internal data structure that acts as a map but also allows getting / removing @@ -78,7 +78,8 @@ angular.module('ui.bootstrap.modal', []) return { restrict: 'EA', scope: { - index: '@' + index: '@', + animate: '=' }, replace: true, transclude: true, @@ -105,8 +106,8 @@ angular.module('ui.bootstrap.modal', []) }; }]) - .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', - function ($document, $compile, $rootScope, $$stackedMap) { + .factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap', + function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) { var OPENED_MODAL_CLASS = 'modal-open'; @@ -139,17 +140,46 @@ angular.module('ui.bootstrap.modal', []) openedWindows.remove(modalInstance); //remove window DOM element - modalWindow.modalDomEl.remove(); + removeAfterAnimating(modalWindow.modalDomEl, modalWindow.modalScope, function () { + //remove backdrop if no longer needed + if (backdropDomEl && backdropIndex() == -1) { + removeAfterAnimating(backdropDomEl, backdropScope); + backdropDomEl = undefined; + } + }); body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0); + } + + function removeAfterAnimating(domEl, scope, done) { + // Closing animation + scope.animate = false; - //remove backdrop if no longer needed - if (backdropDomEl && backdropIndex() == -1) { - backdropDomEl.remove(); - backdropDomEl = undefined; + var transitionEndEventName = $transition.transitionEndEventName; + if (transitionEndEventName) { + // transition out + var timeout = $timeout(afterAnimating, 500); + + domEl.bind(transitionEndEventName, function () { + $timeout.cancel(timeout); + afterAnimating(); + scope.$apply(); + }); + } else { + // Ensure this call is async + $timeout(afterAnimating, 0); } - //destroy scope - modalWindow.modalScope.$destroy(); + function afterAnimating() { + if (afterAnimating.done) { + return; + } + afterAnimating.done = true; + + domEl.remove(); + if (done) { + done(); + } + } } $document.bind('keydown', function (evt) { @@ -185,6 +215,7 @@ angular.module('ui.bootstrap.modal', []) var angularDomEl = angular.element('
'); angularDomEl.attr('window-class', modal.windowClass); angularDomEl.attr('index', openedWindows.length() - 1); + angularDomEl.attr('animate', 'animate'); angularDomEl.html(modal.content); var modalDomEl = $compile(angularDomEl)(modal.scope); diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index 96abff51a1..c4183c1054 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -104,6 +104,7 @@ describe('$modal', function () { function dismiss(modal, reason) { modal.dismiss(reason); + $timeout.flush(); $rootScope.$digest(); } @@ -120,6 +121,9 @@ describe('$modal', function () { dismiss(modal, 'closing in test'); expect($document).toHaveModalsOpen(0); + + // Backdrop animation + $timeout.flush(); expect($document).not.toHaveBackdrop(); }); @@ -135,6 +139,9 @@ describe('$modal', function () { dismiss(modal, 'closing in test'); expect($document).toHaveModalsOpen(0); + + // Backdrop animation + $timeout.flush(); expect($document).not.toHaveBackdrop(); }); @@ -144,6 +151,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); triggerKeyDown($document, 27); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0); @@ -155,6 +163,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); $document.find('body > div.modal').click(); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0);