How do I use Bootstrap theme's modal.jquery.ui.bridge's mapped events? I am using a Bootstrap subtheme, and would like to trigger an event when a modal is closed.

Reviewing the main theme's modal.jquery.ui.bridge.js file, I see that the bridge is intending to map jQuery UI events (beforeClose, close) to framework events (hide.bs.modal, hidden.bs.modal).

I'm struggling with figuring out how to properly declare the data-dialog-options attribute of the triggering link to pass this event through the bridge.

In the JS file, this.options.jQueryUiBridge is true when typeof options === 'object'. My guess is that my options are not typeof object.

My attributes array on the Url object is

  $attributes = [
    'class' =&gt; ['use-ajax'],
    'data-dialog-type' =&gt; 'modal',
    'data-dialog-options' =&gt; Json::encode([
      'dialogClass' =&gt; 'modal-content--special-class',
      'beforeClose' =&gt; 'function() {console.log("closed");}',
    ]),
  ];


which results in the rendered link

&lt;a href="/my-content-route" class="use-ajax" data-dialog-type="modal" data-dialog-options="{&amp;quot;dialogClass&amp;quot;:&amp;quot;modal-content--space-wizard&amp;quot;,&amp;quot;beforeClose&amp;quot;:&amp;quot;function() {console.log(\u0022closed\u0022);}&amp;quot;}"&gt;My Link Text&lt;/a&gt;


What am I missing?
The technical reason it's not doing anything is that the value is retrieved as a string and not as a Function object.

You shouldn't pass anonymous functions in data attributes.

This would require whatever implements the callback (jQuery UI/Bootstrap) property to use eval() or some other sort of method to evaluate the callback.

This, of course, has major security implications and why it doesn't work.

All callbacks should be added via JavaScript.

If you don't have control over specific implementation, you can always set a global listening event and then use some sort of unique identifier on the modal to trigger a custom callback.

Example:

(function ($){
  var $document = $(document);

  // Create a custom beforeClose handler.
  var customBeforeCloseHandler = function (e) {
    console.log('closed');

    // Optionally, you should be able to stop it from closing
    // if that is the desired outcome by using the following:
    // e.preventDefault();
  };

  // jQuery UI Dialog Events.
  // @see http://api.jqueryui.com/dialog/#events
  $document.on('dialogbeforeclose', '.ui-dialog', function(e) {
    var $dialog = $(e.target);

    // Check if this dialog is ours.
    if ($dialog.is('.modal-content--special-class')) {
      // Proxy this event to our custom handler.
      customBeforeCloseHandler(e, ui);
    }
  });

  // Bootstrap Modal Events.
  // @see https://getbootstrap.com/docs/3.4/javascript/#modals-events
  $document.on('hide.bs.modal', '.modal', function (e) {
    var $modal = $(e.target);

    // Check if this modal is ours.
    if ($modal.find('.modal-content').is('.modal-content--special-class')) {
      // Proxy this event to our custom handler.
      customBeforeCloseHandler(e);
    }
  });

})(jQuery);


Given that the Drupal Bootstrap base theme has the jQuery UI Dialog bridge, I would recommend binding using jQuery UI Dialog events so it will work even without Bootstrap. That being said, if you can't seem to get that to work... using Bootstrap's events should work just as well.

However, don't use both as that would likely trigger your custom handler for both events.