Is there a way to automatically close Angular UI Bootstrap modal when route changes?

AngularjsAngular UiAngular Ui-Bootstrap

Angularjs Problem Overview


I've got links in templates inside modals. When I click them, the current page changes, but the overlay and modal stay. I could add ng-click="dimiss()" to every link in all templates in modals, but is there a better way? E.g. to close it automatically on successful route change or add just one ng-click per template to handle all links?

Angularjs Solutions


Solution 1 - Angularjs

If you want all the opened modals to be closed whenever a route is changed successfully, you could do it in one central place by listening to the $routeChangeSuccess event, for example in a run block of your app:

var myApp = angular.module('app', []).run(function($rootScope, $uibModalStack) {
  $uibModalStack.dismissAll();
}); 

Here you can see that the $uibModalStack service gets injected on which you can call the dismissAll method - this call will close all the currently opened modals.

So, yes, you can handle modals closing centrally, in one place, with one line of code :-)

Solution 2 - Angularjs

A better way is to see that whenever a Popup (Modal) is open, on Browser Back button click (or Keyboard Back), we stop the URL change and just close the Popup. This works for a better User Experience in my Project.

The Browser Back button works normally if there is no Modal opened.

use:

$uibModalStack.dismiss(openedModal.key);

or

$uibModalStack.dismissAll;

Sample code:

.run(['$rootScope', '$uibModalStack',
    function ($rootScope,  $uibModalStack) {       
        // close the opened modal on location change.
        $rootScope.$on('$locationChangeStart', function ($event) {
            var openedModal = $uibModalStack.getTop();
            if (openedModal) {
                if (!!$event.preventDefault) {
                    $event.preventDefault();
                }
                if (!!$event.stopPropagation) {
                    $event.stopPropagation();
                }
                $uibModalStack.dismiss(openedModal.key);
            }
        });
    }]);

Solution 3 - Angularjs

I don't actually use Angular UI Bootstrap, but from looking at the docs, it looks like there is a close() method on the $modalInstance object.

So taking the example from the docs, this should work:

var ModalInstanceCtrl = function ($scope, $modalInstance, items) {
    $scope.items = items;
    $scope.selected = {
        item: $scope.items[0]
    };
    $scope.ok = function () {
        $modalInstance.close($scope.selected.item);
    };
    $scope.cancel = function () {
        $modalInstance.dismiss('cancel');
    };

    // this will listen for route changes and call the callback
    $scope.$on('$routeChangeStart', function(){
        $modalInstance.close();
    });
};

Hope that helps.

Solution 4 - Angularjs

I resolved this issue by doing something like this:

$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
$modalStack.dismissAll();
});

Solution 5 - Angularjs

I am keeping this logic in the modal controller. You can listen to $locationChangeStart event and close modal there. It is also good to remove listener after, especially if you have registered a listener on $rootScope:

angular.module('MainApp').controller('ModalCtrl',['$scope','$uibModalInstance',
function ($scope, $uibModalInstance) {
  
  var dismissModalListener = $scope.$on('$locationChangeStart', function () {
    $uibModalInstance.close();
  });

  $scope.$on('$destroy', function() {
    dismissModalListener();
  });

}]);

Solution 6 - Angularjs

check for the respective route condition in the event $stateChangeSuccess and then close the open bootstrap modals globally using the class like this:

$rootScope.$on('$stateChangeSuccess',
function(event, toState, toParams, fromState, fromParams){
//hide any open bootstrap modals
  angular.element('.inmodal').hide();
});

If you want to hide any other modals such as angular material dialog ($mdDialog) & sweet alert dialog's use angular.element('.modal-dialog').hide(); & angular.element('.sweet-alert').hide();

Solution 7 - Angularjs

Adding this an alternative answer.

Depending on your project, using $uibModalStack.dismissAll() could trigger an error message.

As explained by JB Nizet in this answer, it is caused by dismissAll() rejecting a promise, leading to a 'failure' callback as opposed to a 'success' callback triggered by close().

Said promise rejection could trigger a possibly unwanted error handling procedure.

Given there is no closeAll() in $uibModalStack, I used this:

    var modal = $uibModalStack.getTop();
    while (modal && this.close(modal.key)) {
      modal = this.getTop();
    }

This is the same behaivour as $uibModalStack.dismissAll() but utilizes.close() instead of .dismiss().

I couldn't find any documentation describing public methods for $uibModalStack, thus, in case anybody is interested in using/looking at other methods available on $uibModalStack.

It'll likely be located in \node-modules\angular-ui-boostrap\dist\ui-boostrap-tpls.js and dismissAll() is @ line 4349

Took me a while to find it.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionszimekView Question on Stackoverflow
Solution 1 - Angularjspkozlowski.opensourceView Answer on Stackoverflow
Solution 2 - AngularjsGamaSharmaView Answer on Stackoverflow
Solution 3 - AngularjstennisgentView Answer on Stackoverflow
Solution 4 - Angularjskiran.gilvazView Answer on Stackoverflow
Solution 5 - AngularjsKhrystyna SkvarokView Answer on Stackoverflow
Solution 6 - AngularjshakunaView Answer on Stackoverflow
Solution 7 - AngularjsmrOakView Answer on Stackoverflow