Skip to content

Commit ae7cd5a

Browse files
committed
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 angular/angular.js#5109 ). Closes angular-ui#1341
1 parent 41bea46 commit ae7cd5a

File tree

2 files changed

+28
-7
lines changed

2 files changed

+28
-7
lines changed

src/modal/modal.js

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
angular.module('ui.bootstrap.modal', [])
1+
angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
22

33
/**
44
* A helper, internal data structure that acts as a map but also allows getting / removing
@@ -101,8 +101,8 @@ angular.module('ui.bootstrap.modal', [])
101101
};
102102
}])
103103

104-
.factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap',
105-
function ($document, $compile, $rootScope, $$stackedMap) {
104+
.factory('$modalStack', ['$transition', '$timeout', '$document', '$compile', '$rootScope', '$$stackedMap',
105+
function ($transition, $timeout, $document, $compile, $rootScope, $$stackedMap) {
106106

107107
var backdropjqLiteEl, backdropDomEl;
108108
var backdropScope = $rootScope.$new(true);
@@ -133,16 +133,34 @@ angular.module('ui.bootstrap.modal', [])
133133
openedWindows.remove(modalInstance);
134134

135135
//remove window DOM element
136-
modalWindow.modalDomEl.remove();
136+
// Scope should be destroyed when element is removed
137+
removeAfterAnimating(modalWindow.modalDomEl, modalWindow.modalScope);
137138

138139
//remove backdrop if no longer needed
139140
if (backdropDomEl && backdropIndex() == -1) {
140-
backdropDomEl.remove();
141+
removeAfterAnimating(backdropDomEl, backdropScope);
141142
backdropDomEl = undefined;
142143
}
144+
}
143145

144-
//destroy scope
145-
modalWindow.modalScope.$destroy();
146+
function removeAfterAnimating(domEl, scope) {
147+
scope.$destroy();
148+
domEl.removeClass('in');
149+
150+
var transitionEndEventName = $transition.transitionEndEventName;
151+
if (transitionEndEventName) {
152+
// transition out
153+
var timeout = $timeout(function () {
154+
domEl.remove();
155+
}, 500);
156+
157+
domEl.bind(transitionEndEventName, function () {
158+
$timeout.cancel(timeout);
159+
domEl.remove();
160+
});
161+
} else {
162+
domEl.remove();
163+
}
146164
}
147165

148166
$document.bind('keydown', function (evt) {

src/modal/test/modal.spec.js

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ describe('$modal', function () {
103103

104104
function dismiss(modal, reason) {
105105
modal.dismiss(reason);
106+
$timeout.flush();
106107
$rootScope.$digest();
107108
}
108109

@@ -143,6 +144,7 @@ describe('$modal', function () {
143144
expect($document).toHaveModalsOpen(1);
144145

145146
triggerKeyDown($document, 27);
147+
$timeout.flush();
146148
$rootScope.$digest();
147149

148150
expect($document).toHaveModalsOpen(0);
@@ -154,6 +156,7 @@ describe('$modal', function () {
154156
expect($document).toHaveModalsOpen(1);
155157

156158
$document.find('body > div.modal-backdrop').click();
159+
$timeout.flush();
157160
$rootScope.$digest();
158161

159162
expect($document).toHaveModalsOpen(0);

0 commit comments

Comments
 (0)