From dc3d67bf92b189f1b65267e408447e68cee0d543 Mon Sep 17 00:00:00 2001 From: Umut Benzer Date: Mon, 2 Dec 2013 10:57:00 +0200 Subject: [PATCH 1/3] feat(modal): prevent location change if modal is open You are in a state that modal is open. User accidentally clicks back button on the browser. You use a state manager, for example ui-router, so page transition happens, without page refresh. Now, you have a completely different page, completely different state, but you have modal open. This PR prevents URL change if modal is open. --- src/modal/modal.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/modal/modal.js b/src/modal/modal.js index 12837f3fa9..9a84595959 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -158,6 +158,13 @@ angular.module('ui.bootstrap.modal', []) } }); + $rootScope.$on('$locationChangeStart', function(evt) { + /* If modal is open, prevent location change! */ + if(openedWindows.length() > 0) { + evt.preventDefault(); + } + }); + $modalStack.open = function (modalInstance, modal) { openedWindows.add(modalInstance, { From d262df5c31401d3170e01d037f52209eaadf9c76 Mon Sep 17 00:00:00 2001 From: Umut Benzer Date: Tue, 10 Dec 2013 14:12:04 +0200 Subject: [PATCH 2/3] feat(modal): modal should close if navigation occurs You are in a state that modal is open. User clicks back button on the browser. You use a state manager, for example ui-router, so page transition happens, without page refresh. Now, you have a completely different page, completely different state, but you have modal open. To prevent this situation, modals are closed automatically on `$locationChangeStart` Developer may prevent this behaviour by passing `closeOnNavigation: false` as option. --- .gitignore | 1 + src/modal/docs/readme.md | 1 + src/modal/modal.js | 32 ++++++++++++++---- src/modal/test/modal.spec.js | 64 ++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 45aee72754..01a69165c3 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ lib-cov *.swp *.swo .DS_Store +.idea pids logs diff --git a/src/modal/docs/readme.md b/src/modal/docs/readme.md index 2e69a34a8f..e7d987ee5d 100644 --- a/src/modal/docs/readme.md +++ b/src/modal/docs/readme.md @@ -11,6 +11,7 @@ The `$modal` service has only one method: `open(options)` where available option * `backdrop` - controls presence of a backdrop. Allowed values: true (default), false (no backdrop), `'static'` - backdrop is present but modal window is not closed when clicking outside of the modal window. * `keyboard` - indicates whether the dialog should be closable by hitting the ESC key, defaults to true * `windowClass` - additional CSS class(es) to be added to a modal window template +* `closeOnNavigation` - If true, modal will be closed on page navigation, defaults to true The `open` method returns a modal instance, an object with the following properties: diff --git a/src/modal/modal.js b/src/modal/modal.js index 9a84595959..924391b54a 100644 --- a/src/modal/modal.js +++ b/src/modal/modal.js @@ -159,10 +159,27 @@ angular.module('ui.bootstrap.modal', []) }); $rootScope.$on('$locationChangeStart', function(evt) { - /* If modal is open, prevent location change! */ - if(openedWindows.length() > 0) { - evt.preventDefault(); + /* Close modals whose closeOnNavigation is true */ + var opened = openedWindows.keys(); + if(opened.length === 0) { return; } + var tbClosed = []; + + for (var i = 0; i < opened.length; i++) { + var modal = openedWindows.get(opened[i]); + if (modal.value.closeOnNavigation) { + tbClosed.push(modal.key); + } + } + + if(tbClosed.length > 0) { + /* Call apply function for once, not for each close operation. */ + $rootScope.$apply(function () { + for (var i = 0; i < tbClosed.length; i++) { + $modalStack.dismiss(tbClosed[i]); + } + }); } + }); $modalStack.open = function (modalInstance, modal) { @@ -171,7 +188,8 @@ angular.module('ui.bootstrap.modal', []) deferred: modal.deferred, modalScope: modal.scope, backdrop: modal.backdrop, - keyboard: modal.keyboard + keyboard: modal.keyboard, + closeOnNavigation: modal.closeOnNavigation }); if (backdropIndex() >= 0 && !backdropDomEl) { @@ -219,7 +237,8 @@ angular.module('ui.bootstrap.modal', []) var $modalProvider = { options: { backdrop: true, //can be also false or 'static' - keyboard: true + keyboard: true, + closeOnNavigation: true }, $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) { @@ -299,7 +318,8 @@ angular.module('ui.bootstrap.modal', []) content: tplAndVars[0], backdrop: modalOptions.backdrop, keyboard: modalOptions.keyboard, - windowClass: modalOptions.windowClass + windowClass: modalOptions.windowClass, + closeOnNavigation: modalOptions.closeOnNavigation }); }, function resolveError(reason) { diff --git a/src/modal/test/modal.spec.js b/src/modal/test/modal.spec.js index efc0ccff58..7949b94a98 100644 --- a/src/modal/test/modal.spec.js +++ b/src/modal/test/modal.spec.js @@ -348,6 +348,70 @@ describe('$modal', function () { }); }); + describe('navigation', function () { + + it('should close modals if closeOnNavigation option is set to true', function () { + open({ + template: '
Should close on navigate
', + closeOnNavigation: true + }); + + expect($document).toHaveModalsOpen(1); + + $rootScope.$broadcast("$locationChangeStart"); + $rootScope.$digest(); + + expect($document).toHaveModalsOpen(0); + }); + + it('should close modals by default', function () { + open({ + template: '
Should close on navigate
' + }); + + expect($document).toHaveModalsOpen(1); + + $rootScope.$broadcast("$locationChangeStart"); + $rootScope.$digest(); + + expect($document).toHaveModalsOpen(0); + }); + + it('should not close modals if closeOnNavigation option is set to false', function () { + open({ + template: '
Should not close on navigate
', + closeOnNavigation: false + }); + + expect($document).toHaveModalsOpen(1); + + $rootScope.$broadcast("$locationChangeStart"); + $rootScope.$digest(); + + expect($document).toHaveModalsOpen(1); + }); + + it('should work with multiple modals', function () { + open({ + template: '
Should close 1
', + closeOnNavigation: true + }); + open({ + template: '
Should close 2
' + }); + open({ + template: '
Should not close
', + closeOnNavigation: false + }); + expect($document).toHaveModalsOpen(3); + + $rootScope.$broadcast("$locationChangeStart"); + $rootScope.$digest(); + + expect($document).toHaveModalsOpen(1); + }); + }); + describe('backdrop', function () { it('should not have any backdrop element if backdrop set to false', function () { From a3059ae7eb4c6f71c621038644eadb8750428038 Mon Sep 17 00:00:00 2001 From: Umut Benzer Date: Wed, 11 Dec 2013 13:42:29 +0200 Subject: [PATCH 3/3] feat(modal): cleaning up .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 01a69165c3..45aee72754 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ lib-cov *.swp *.swo .DS_Store -.idea pids logs