Angularjs Bootstrap modal closing call when clicking outside/esc

51,248

Solution 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.

Solution 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.

Solution 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!!!");
      });

Solution 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.

Share:
51,248

Related videos on Youtube

bryan
Author by

bryan

Updated on October 20, 2020

Comments

  • bryan
    bryan over 3 years

    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');
    };
    
    • Sven van den Boogaart
      Sven van den Boogaart about 9 years
      Catch the triggers, add preventDefault(); add the code you want to run end with the close modal command.
  • bryan
    bryan about 9 years
    Thanks PSL! This works perfect and thanks for the extra information.
  • Kushal Suthar
    Kushal Suthar almost 7 years
    I tried your given solution but it's not working for me. Can you please provide plunker.plnkr.co/edit/APp2N5?p=preview
  • Tiago
    Tiago almost 7 years
    @kushalsuthar 2 reasons: you had older angular.js and ui-bootstrap versions (see index.html), and you need to give your modal a dedicated controller. Here's the updated punker: plnkr.co/edit/8MThqCxeh9pEf37eBFSB?p=preview
  • Kushal Suthar
    Kushal Suthar almost 7 years
    Actually I got that solution.Thaks for your reply.
  • Vinoth
    Vinoth almost 7 years
    Hi PSL, can you answer this question stackoverflow.com/questions/43583136/…
  • Vinoth
    Vinoth almost 7 years
    Hi Yashika Garg, can you answer this question stackoverflow.com/questions/43583136/…
  • Vinoth
    Vinoth almost 7 years
    Hi Tiago, can you answer this question stackoverflow.com/questions/43583136/…
  • PSL
    PSL almost 7 years
    @Vinoth You should use type="button" for your ok and cancel buttons as they are inside the form. Default button type is submit and will result in form post event which is what you are seeing as page refresh. Alternatively you can also get the $event and preventDefault() on these methods.
  • Vinoth
    Vinoth almost 7 years
    @PSL, I tried that one "type="button" but no improvement