From a36f1fbfe9aaea39a0a6111b4f7603035d8eff3b Mon Sep 17 00:00:00 2001 From: Chris Chua Date: Sun, 15 Dec 2013 23:04:56 -0800 Subject: [PATCH] fix(modal): Support close animation Tests now have to wait for animation before checking for open modals. Tests should actually check for the 'in' class. Setting up both the transition end handler and a timeout ensures that the modal is always closed even if there was an issue with the transition. This was the implementation used by Twitter Bootstrap modal JS code. Note that unbinding the transition end event within the event handler isn't necessary, and, worse is currently buggy in jqLite (see https://github.com/angular/angular.js/pull/5109 ). Note that the scope is already destroyed when the dom is removed so the $destroy call isn't needed. Closes #1341 --- src/modal/modal.js | 37 ++++++++++++++++++++++++++++-------- src/modal/test/modal.spec.js | 3 +++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/modal/modal.js b/src/modal/modal.js index 12ddfa8ee4..68afa93d1c 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 @@ -87,7 +87,8 @@ angular.module('ui.bootstrap.modal', []) return { restrict: 'EA', scope: { - index: '@' + index: '@', + animate: '=' }, replace: true, transclude: true, @@ -106,8 +107,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'; @@ -140,17 +141,36 @@ angular.module('ui.bootstrap.modal', []) openedWindows.remove(modalInstance); //remove window DOM element - modalWindow.modalDomEl.remove(); + removeAfterAnimating(modalWindow.modalDomEl, modalWindow.modalScope); body.toggleClass(OPENED_MODAL_CLASS, openedWindows.length() > 0); //remove backdrop if no longer needed if (backdropDomEl && backdropIndex() == -1) { - backdropDomEl.remove(); + removeAfterAnimating(backdropDomEl, backdropScope); backdropDomEl = undefined; } + } - //destroy scope - modalWindow.modalScope.$destroy(); + function removeAfterAnimating(domEl, scope) { + // Closing animation + $timeout(function () { + scope.animate = false; + }); + + var transitionEndEventName = $transition.transitionEndEventName; + if (transitionEndEventName) { + // transition out + var timeout = $timeout(function () { + domEl.remove(); + }, 500); + + domEl.bind(transitionEndEventName, function () { + $timeout.cancel(timeout); + domEl.remove(); + }); + } else { + domEl.remove(); + } } $document.bind('keydown', function (evt) { @@ -186,6 +206,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 697eec6861..85a0a2bf1c 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(); } @@ -144,6 +145,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); triggerKeyDown($document, 27); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0); @@ -155,6 +157,7 @@ describe('$modal', function () { expect($document).toHaveModalsOpen(1); $document.find('body > div.modal-backdrop').click(); + $timeout.flush(); $rootScope.$digest(); expect($document).toHaveModalsOpen(0);