Angularjs Bootstrap modal closing call when clicking outside/esc

All we need is an easy explanation of the problem, so here it is.

I am using the Angular-ui/bootstrap modal in my project.

Here is my modal:

$scope.toggleModal = function () {
    $scope.theModal = $modal.open({
        animation: true,
        templateUrl: 'pages/templates/modal.html',
        size: "sm",
        scope: $scope
    });
}

One is able to close the modal by clicking the ESC button or clicking outside the modal area. Is there a way to run a function when this happens? I am not quite sure how to catch the sort of closing.

I know that I can manually dismiss a modal by having a ng-click="closeModal()" like this:

$scope.closeModal = function () {
    $scope.theModal.dismiss('cancel');
};

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

Yes you can. It causes a dismiss event and the promise is rejected in that case. Also, note that the $modal.open() method returns an object that has a result property that is a promise.

With the promise you can…

//This will run when modal dismisses itself when clicked outside or
//when you explicitly dismiss the modal using .dismiss function.
$scope.theModal.result.catch(function(){
    //Do stuff with respect to dismissal
});

//Runs when modal is closed without being dismissed, i.e when you close it
//via $scope.theModal.close(...);
$scope.theModal.result.then(function(datapassedinwhileclosing){
    //Do stuff with respect to closure
});

as a shortcut you could write:

 $scope.theModal.result.then(doClosureFn, doDismissFn);

See ref

The open method returns a modal instance, an object with the following properties:

  • close(result) – a method that can be used to close a modal, passing a result
  • dismiss(reason) – a method that can be used to dismiss a modal, passing a reason
  • result – a promise that is resolved when a modal is closed and rejected when a modal is dismissed
  • opened – a promise that is resolved when a modal gets opened after downloading content’s template and resolving all variables
    ‘rendered’ – a promise that is resolved when a modal is rendered.

Method 2

Old question, but if you want to add confirmation dialogs on various close actions, add this to your modal instance controller:

$scope.$on('modal.closing', function(event, reason, closed) {
    console.log('modal.closing: ' + (closed ? 'close' : 'dismiss') + '(' + reason + ')');
    var message = "You are about to leave the edit view. Uncaught reason. Are you sure?";
    switch (reason){
        // clicked outside
        case "backdrop click":
            message = "Any changes will be lost, are you sure?";
            break;

        // cancel button
        case "cancel":
            message = "Any changes will be lost, are you sure?";
            break;

        // escape key
        case "escape key press":
            message = "Any changes will be lost, are you sure?";
            break;
    }
    if (!confirm(message)) {
        event.preventDefault();
    }
});

I have a close button on the top right of mine, which triggers the “cancel” action. Clicking on the backdrop (if enabled), triggers the cancel action. You can use that to use different messages for various close events. Thought I’d share in case it’s helpful for others.

Method 3

You can use the “result” promise returned by $modal.open() method. As bellow:

 $scope.toggleModal = function () {
      $scope.theModal = $modal.open({
          animation: true,
          templateUrl: 'pages/templates/modal.html',
          size: "sm",
          scope: $scope
      });

      $scope.theModal.result.then(function(){
          console.log("Modal Closed!!!");
      }, function(){
          console.log("Modal Dismissed!!!");
      });
 }

Also you can use “finally” callback of “result” promise as below:

     $scope.theModal.result.finally(function(){
          console.log("Modal Closed!!!");
      });

Method 4

In my case, when clicking off the modal, we wanted to display a prompt warning the user that doing so would discard all unsaved data in the modal form. To do this, set the following options on the modal:

var myModal = $uibModal.open({
          controller: 'MyModalController',
          controllerAs: 'modal',
          templateUrl: 'views/myModal.html',
          backdrop: 'static',
          keyboard: false,
          scope: modalScope,
          bindToController: true,
        });

This prevents the modal from closing when clicking off:

backdrop: 'static'

And this prevents the modal from closing when hitting ‘esc’:

keyboard: false

Then in the modal controller, add a custom “cancel” function – in my case a sweet alert pops up asking if the user wishes to close the modal:

  modal.cancel = function () {
    $timeout(function () {
      swal({
        title: 'Attention',
        text: 'Do you wish to discard this data?',
        type: 'warning',
        confirmButtonText: 'Yes',
        cancelButtonText: 'No',
        showCancelButton: true,
      }).then(function (confirm) {
        if (confirm) {
          $uibModalInstance.dismiss('cancel');
        }
      });
    })
  };

And lastly, inside the modal controller, add the following event listeners:

  var myModal = document.getElementsByClassName('modal');
  var myModalDialog = document.getElementsByClassName('modal-dialog');

  $timeout(function () {
    myModal[0].addEventListener("click", function () {
      console.log('clicked')
      modal.cancel();
    })

    myModalDialog[0].addEventListener("click", function (e) {
      console.log('dialog clicked')
      e.stopPropagation();
    })
  }, 100);

“myModal” is the element you want to call the modal.cancel() callback function on.
“myModalDialog” is the modal content window – we stop the event propagation for this element so it won’t bubble up to “myModal”.

This only works for clicking off the modal (in other words clicking the backdrop). Hitting ‘esc’ will not trigger this callback.

Method 5

Instead of ng-click="closeModal()" you can try ng-click="$dismiss()"

<button ng-click="$dismiss()">Close</button>

Method 6

We can call jquery ‘On’ event as well in the controller like this. here “viewImageModal” is the id of modal popup.

constructor($scope: AuditAppExtension.IActionPlanScope, dataSvc: ActionPlanService, Upload, $timeout, $mdToast: any) {

            $('#viewImageModal').on('shown.bs.modal', function (e) {
                console.log("shown", e);

                $scope.paused = false;
                $modal.find('.carousel').carousel('cycle');
            });

            $('#viewImageModal').on('hide.bs.modal', function (e) {
                console.log("hide", e);

                return true;
            });
}

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply