Data attribute to call function when modal dialog is closed

by user1438038   Last Updated January 14, 2018 12:26 PM

I'm using Bootstrap in conjunction with AngularJS to open modal dialogs. To activate a modal without writing JavaScript code, I use the data attributes as described in the documentation. This is a very convenient way, since I do not need to show/hide the dialog manually.

<button type="button" data-toggle="modal" data-target="#myModal">Launch modal</button>

Now I would like to call a method when the modal dialog is closed. With an explicit close button, this is no problem. However, when the user clicks outside of the dialog or presses the Esc key, I cannot trigger any function explicitly.

I know that I can use jQuery or Angular's $uibModal to listen for a dismiss event, but this makes the entire project more complex. I'd rather have it all in one place. I do not want to mix things up, so using jQuery within my AngularJS project is not an option. The solution I'm stuck with right now, is using $uibModal to open() the dialog manually and catching the result to handle user-invoked dismiss.

My question:

How can I call a function when a modal dialog is closed without introducing too much clutter?

What I have in mind looks like this (imaginary data-dismiss-callback):

<button type="button" data-toggle="modal"
                      data-target="#myModal"
                      data-dismiss-callback="handleCloseEvent()">Launch modal</button>


Answers 1


As we want to attach a specified behavior (custom callback) to the button, then directive is the best candidate who can help us with achieving this.

We will be listening to show.bs.modal and hide.bs.modal/hidden.bs.modal events: the first one will help us to determine if the modal was opened using the corresponding button and the second one is the place where we want to call the passed callback function.

Here is a working example of modalDismissCallback directive (due to normalization, we can't name it dataDismissCallback):

angular.module('myDemoApp', [])
    .controller('myCtrl', [function () {
        var ctrl = this;
        ctrl.testVar = 2;
        ctrl.onModalDismiss = onModalDismiss;

        function onModalDismiss(a, e) {
            console.log(arguments);
        }

        return ctrl;
    }])
    .directive('modalDismissCallback', [function modalDismissCallback() {
        return {
            restrict: 'A',
            scope: {
                modalDismissCallback: '&'
            },
            link: function (scope, element) {
                var modal = angular.element(element.data('target'));

                modal.on('show.bs.modal', onShow);
                modal.on('hide.bs.modal', onHide);

                scope.$on('$destroy', function () {
                  modal.off('show.bs.modal', onShow);
                  modal.off('hide.bs.modal', onHide);
                });
                
                var shouldCall = false;

                function onShow(e) {
                    shouldCall = e.relatedTarget === element[0];
                }

                function onHide(e) {
                    if (angular.isFunction(scope.modalDismissCallback) && shouldCall) {
                        scope.$event = e;
                        scope.$applyAsync(function () {
                            scope.modalDismissCallback.apply(this, arguments);
                        });
                    }
                }
            }
        }
    }]);
<link rel="stylesheet" type="text/css" href="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta.3/css/bootstrap.min.css">

<body ng-app="myDemoApp">

<div ng-controller="myCtrl as $ctrl">

    <button type="button" class="btn btn-default"
            data-toggle="modal"
            data-target="#myModal"
            modal-dismiss-callback="$ctrl.onModalDismiss($ctrl.testVar, $event)">Launch modal
    </button>
    
    <button type="button" class="btn btn-default"
            data-toggle="modal"
            data-target="#myModal">Launch modal wo callback
    </button>

    <div id="myModal" class="modal fade bd-example-modal-sm" tabindex="-1" role="dialog"
         aria-labelledby="mySmallModalLabel" aria-hidden="true">


        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h4 class="modal-title" id="myModalLabel">Modal title</h4>
                </div>
                <div class="modal-body">
                    <div ng-include="'template.html'"></div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
                </div>
            </div>
        </div>

    </div>
</div>

<script type="text/ng-template" id="template.html"><h5>Hello from ng-template!</h5></script>
</body>




<script type="text/javascript" src="//code.jquery.com/jquery-3.1.1.slim.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-beta.3/js/bootstrap.min.js"></script>

Stanislav Kvitash
Stanislav Kvitash
January 14, 2018 12:25 PM

Related Questions


bootstrap modal as jquery object

Updated September 17, 2017 10:26 AM

React Boostrap Modal not opening on click

Updated March 02, 2017 04:26 AM


Open youtube videos in bootstrap modal

Updated November 24, 2017 18:26 PM