Skip to content
This repository was archived by the owner on May 29, 2019. It is now read-only.

Commit a5a82d9

Browse files
Nathan Williamswesleycho
Nathan Williams
authored andcommittedMar 16, 2015
feat(Modal): Add a vetoable modal.closing event
Add tests Includes tests, refinements to the documentation and a fix for an inconsistent variable name. Fix test bug
1 parent ab919f9 commit a5a82d9

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed
 

Diff for: ‎src/modal/docs/readme.md

+5
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,8 @@ In addition the scope associated with modal's content is augmented with 2 method
3030
* `$dismiss(reason)`
3131

3232
Those methods make it easy to close a modal window without a need to create a dedicated controller.
33+
34+
Finally, a `modal.closing` event is broadcast to the modal scope before the modal closes. If the listener calls
35+
preventDefault on the event, then the modal will remain open. The $close and $dismiss methods return true if the
36+
event was allowed. The event itself includes a parameter for the result/reason and a boolean parameter that indicates
37+
whether the modal is being closed (true) or dismissed.

Diff for: ‎src/modal/modal.js

+13-6
Original file line numberDiff line numberDiff line change
@@ -290,26 +290,33 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
290290
body.addClass(OPENED_MODAL_CLASS);
291291
};
292292

293+
function broadcastClosing(modalWindow, resultOrReason, closing) {
294+
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
295+
}
296+
293297
$modalStack.close = function (modalInstance, result) {
294298
var modalWindow = openedWindows.get(modalInstance);
295-
if (modalWindow) {
299+
if (modalWindow && broadcastClosing(modalWindow, result, true)) {
296300
modalWindow.value.deferred.resolve(result);
297301
removeModalWindow(modalInstance);
302+
return true;
298303
}
304+
return !modalWindow;
299305
};
300306

301307
$modalStack.dismiss = function (modalInstance, reason) {
302308
var modalWindow = openedWindows.get(modalInstance);
303-
if (modalWindow) {
309+
if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
304310
modalWindow.value.deferred.reject(reason);
305311
removeModalWindow(modalInstance);
312+
return true;
306313
}
314+
return !modalWindow;
307315
};
308316

309317
$modalStack.dismissAll = function (reason) {
310318
var topModal = this.getTop();
311-
while (topModal) {
312-
this.dismiss(topModal.key, reason);
319+
while (topModal && this.dismiss(topModal.key, reason)) {
313320
topModal = this.getTop();
314321
}
315322
};
@@ -370,10 +377,10 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.transition'])
370377
opened: modalOpenedDeferred.promise,
371378
rendered: modalRenderDeferred.promise,
372379
close: function (result) {
373-
$modalStack.close(modalInstance, result);
380+
return $modalStack.close(modalInstance, result);
374381
},
375382
dismiss: function (reason) {
376-
$modalStack.dismiss(modalInstance, reason);
383+
return $modalStack.dismiss(modalInstance, reason);
377384
}
378385
};
379386

Diff for: ‎src/modal/test/modal.spec.js

+70-2
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,17 @@ describe('$modal', function () {
107107
}
108108

109109
function close(modal, result) {
110-
modal.close(result);
110+
var closed = modal.close(result);
111111
$timeout.flush();
112112
$rootScope.$digest();
113+
return closed;
113114
}
114115

115116
function dismiss(modal, reason) {
116-
modal.dismiss(reason);
117+
var closed = modal.dismiss(reason);
117118
$timeout.flush();
118119
$rootScope.$digest();
120+
return closed;
119121
}
120122

121123
describe('basic scenarios with default options', function () {
@@ -607,4 +609,70 @@ describe('$modal', function () {
607609
expect(body).not.toHaveClass('modal-open');
608610
});
609611
});
612+
613+
describe('modal.closing event', function() {
614+
it('should close the modal contingent on the modal.closing event and return whether the modal closed', function() {
615+
var preventDefault;
616+
var modal;
617+
var template = '<div>content</div>';
618+
619+
function TestCtrl($scope) {
620+
$scope.$on('modal.closing', function (event, resultOrReason, closing) {
621+
if (preventDefault) {
622+
event.preventDefault();
623+
}
624+
});
625+
}
626+
627+
modal = open({template: template, controller: TestCtrl});
628+
629+
preventDefault = true;
630+
expect(close(modal, 'result')).toBeFalsy();
631+
expect($document).toHaveModalsOpen(1);
632+
633+
preventDefault = false;
634+
expect(close(modal, 'result')).toBeTruthy();
635+
expect($document).toHaveModalsOpen(0);
636+
637+
modal = open({template: template, controller: TestCtrl});
638+
639+
preventDefault = true;
640+
expect(dismiss(modal, 'result')).toBeFalsy();
641+
expect($document).toHaveModalsOpen(1);
642+
643+
preventDefault = false;
644+
expect(dismiss(modal, 'result')).toBeTruthy();
645+
expect($document).toHaveModalsOpen(0);
646+
});
647+
648+
it('should trigger modal.closing and pass result/reason and closing parameters to the event', function() {
649+
var called;
650+
651+
called = false;
652+
close(open({
653+
template: '<div>content</div>',
654+
controller: function($scope) {
655+
$scope.$on('modal.closing', function(event, resultOrReason, closing) {
656+
called = true;
657+
expect(resultOrReason).toBe('result');
658+
expect(closing).toBeTruthy();
659+
});
660+
}
661+
}), 'result');
662+
expect(called).toBeTruthy();
663+
664+
called = false;
665+
dismiss(open({
666+
template: '<div>content</div>',
667+
controller: function($scope) {
668+
$scope.$on('modal.closing', function(event, resultOrReason, closing) {
669+
called = true;
670+
expect(resultOrReason).toBe('reason');
671+
expect(closing).toBeFalsy();
672+
});
673+
}
674+
}), 'reason');
675+
expect(called).toBeTruthy();
676+
});
677+
});
610678
});

0 commit comments

Comments
 (0)
This repository has been archived.