CTools "Auto" Modal for Non-Link Elements

August 15
blog author

Appno Blogger

Appnovation Coop

There will be a time when you need to display content or form on a modal. CTools and CTools Auto Modal help lessen the work a lot by providing many useful tools, such as making it easy to put a form in a modal (CTools), and any hook_menu() paths that have 'modal' => TRUE defined will have the automatic modal support (CTools Auto Modal).

There are few steps to accomplish this:

1.Define your path in hook_menu().

/**
 * Implements hook_menu().
 */
function my_module_menu() {
 $items['modal-form'] = array(
    'title' => 'My Awesome Form',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('my_module_modal_form'),
    'access arguments' => array('access content'),
    'modal' => TRUE,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

2.Use l() to print out your link. CTools Auto Modal uses hook_preprocess_link() to add the CTools JavaScript so if you just type out a link as a markup, the modal won't work.

print l(t('Click me'), 'modal-form');

3. Click on the link.

4. Enjoy your new modal.

Everything is pretty much taken care of by these 2 modules...However, suppose you really have no choice and cannot use a link for the modal (for example, a link within a link would be invalid), CTools Auto Modal would not work in this case, which means you have to trigger the modal manually. This use case is quite specific but I can imagine some of us would bump into the same problem at some point. It involves a little more steps but is totally doable.

1. Load the CTools JS in the page that would have the modal popup.

2. Print out the element that you want to be the modal trigger, for example: <span id="modal-trigger">Click me</span>

3. Write a custom JS to create an AJAX object for this element. (Put it in the sub-folder js in your module).

(function($) {
Drupal.behaviors.my_module = {
  attach: function(context, settings) {
    $('#modal-trigger:not(.modal-trigger-processed)', context).each(function(i, obj) {
     // The URL to your form or content to be displayed on the modal. Remember to add "nojs" at the end.
      var url = 'modal-form/nojs';                   
      // Create a drupal ajax object
      $(obj).click(Drupal.CTools.Modal.clickAjaxLink);        // This is to pop up the modal as soon as the user clicks the element.
      var element_settings = {};
      element_settings.url = url;         
      element_settings.event = 'click';
      element_settings.progress = { type: 'throbber' };
      var base = url;
      Drupal.ajax[base] = new Drupal.ajax(base, obj, element_settings);
      $(obj).addClass('modal-trigger-processed');           // Add a class to flag that this element has already been processed.
    });
  }
})(jQuery);

4. Load this JS in your page callback.

After step 4, your page callback would look like this:

function my_module_page_callback() {
  // Load Ctools js.
  ctools_include('modal');
  ctools_modal_add_js();
  // Load custom js.
  drupal_add_js(drupal_get_path('module', 'my_module') . '/js/my_module.js');
  $click_me = '' . t('Click me') . '';
  return t('Page Content: !click', array('!click' => $click_me));
}

5. Use a different page callback in the hook_menu().

/**
 * Implements hook_menu().
 */
function my_module_menu() {
 $items['modal-form'] = array(
    'title' => 'My Awesome Form',
    'page callback' => 'my_module_modal_form_trigger',         // Different callback.
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

6. Implement the new page callback.

function my_module_modal_form_trigger() {
  $args = func_get_args();
  $ajax = array_pop($args);
  $form_id = 'my_module_modal_form';
  if ($ajax) {
    $commands = array();
    ctools_include('modal');
    ctools_include('ajax');
    $form_state = array('ajax' => $ajax, 'build_info' => array('args' => $args));
    $commands = ctools_modal_form_wrapper($form_id, $form_state);

    if (empty($commands)) {
      $commands[] = ctools_modal_command_loading();
      if (!empty($_GET['destination'])) {
        $commands[] = ctools_ajax_command_redirect($_GET['destination']);
      }
    }
    print ajax_render($commands);
    exit();
  }
  else {
    // If this is not an AJAX request, return the form as usual.
    array_unshift($args, $form_id);
    return call_user_func_array('drupal_get_form', $args);
  }
}

In summary, you need a new page callback and a custom js to make this work

This will also work if you use a link. So in step 2, replace the markup with l(t('Click me'), 'modal-form/nojs', array('attributes' => array('class' => array('ctools-use-modal'))));. The class "ctools-use-modal" is a flag for CTools to create an AJAX object for this element so the custom js won't be needed.

After seeing this, you probably appreciate the modules more huh, as they really take care all the dirty work for us.