user1438038
user1438038

Reputation: 6059

Data attribute to call function when modal dialog is closed

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>

Upvotes: 3

Views: 2370

Answers (1)

Stanislav Kvitash
Stanislav Kvitash

Reputation: 4622

As we want to attach a specified behavior (custom callback) to the target modal using the button that opens it, 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>

Upvotes: 1

Related Questions