Drupal - Show forms in a modal window

There's 2 good options I know of currently: iframe (in colorbox for example) and CTools. Which option to use depends of the circumstances. I guess this information I found in the CTools modal.html file brings out the main difference:

CTools provides a simple modal that can be used as a popup to place forms. It differs from the normal modal frameworks in that it does not do its work via an iframe. This is both an advantage and a disadvantage. The iframe simply renders normal pages in a sub-browser and they can do their thing. That makes it much easier to put arbitrary pages and forms in a modal. However, the iframe is not very good at actually communicating changes to the main page, so you cannot open the modal, have it do some work, and then modify the page.

I have no personal experienced with CTools on this subject so I can't add anything else to that, but I've implemented the iframe method in a couple of projects. In the most recent one I used the Colorbox plugin to show a few forms created with the Webform module in a popup. I'll add some example code here in case there's some interest.

Link to the form:

<a class="colorbox_form" href="'.url('node/5').'">'.t('Send message').'</a>

Javascript code to open the linked address in a popup:

if ($('a.colorbox_form').length > 0) {
  var link = $("a.colorbox_form").attr('href');
  link = link.concat('?colorbox=true');
  // colorbox=true is attached for later use (read below).
  $("a.colorbox_form").attr('href', link);
  $("a.colorbox_form").colorbox({iframe:true, width:500, height:450});
}

In the theme template file:

function mytheme_preprocess_page(&$variables) {
  // Different template and additional stylsheet for colorbox content.
  if (isset($_GET['colorbox']) && $_GET['colorbox'] == TRUE) {
    $variables['theme_hook_suggestions'] = array('page__iframe'); // page--iframe.tpl.php
    drupal_add_css(path_to_theme() .'/iframe.css');
    $variables['styles'] = drupal_get_css();
  }
}

I attached 'colorbox=true' to the links using javascript so that users with javascript turned off would see the form with the normal template. The iframe template only has messages, title and content printed.

This already works, but I ran into a issue with Webforms: 'colorbox=true' wasn't preserved when the form got submitted. My attempt to fix it:

function mymodule_form_alter(&$form, &$form_state, $form_id) {
  if (isset($_GET['colorbox']) && $_GET['colorbox'] == TRUE) {
    // The id-s of the forms that are possibly shown within a popup.
    $form_ids = array('webform_client_form_2', 'webform_client_form_4');
    if (in_array($form_id, $form_ids)) {
      $form['#submit'][] = 'mymodule_webform_submit';
    }
  }
}

function mymodule_webform_submit(&$form, &$form_state) {
  //drupal_set_message('<pre>'.var_export($form_state['redirect'], TRUE).'</pre>');
  if (!isset($form_state['redirect'])) {
    $form_state['redirect'] = array($_GET['q'], array('query' => array('colorbox' => 'true')));
  }
  else {
    $form_state['redirect'][1]['query'] = array('colorbox' => 'true');
  }
}

Use CTools which can insert a form into a modal when a user clicks a link.

Please check the following tutorial: Insert a form into a pop-up modal with CTools and Drupal 7 which simplifies this process in few steps.

Basically you need to define your hook_menu() callback for your modal form:

$items['mymodule/%ctools_js'] = array(
  'page callback' => 'mymodule_callback',
  'page arguments' => array(1),
  'access callback' => TRUE,
  'type' => MENU_CALLBACK,
);

then create a link generator which returns the HTML code:

/**
 * Helper function to make a link.
 */
function _mymodule_make_link($link_text = '') {
  // Set a default value if no text in supplied.
  if (empty($link_text)) {
    $link_text = 'Magical Modal';
  }

  return '<div id="magical-modal-link">' . l($link_text, 'mymodule/nojs', array('attributes' => array('class' => 'ctools-use-modal'))) . '</div>';
}

so it can be used in your page callback, e.g.:

/**
 * An example page.
 */
function mymodule_page() {
  // Load the modal library and add the modal javascript.
  ctools_include('modal');
  ctools_modal_add_js();
  return _mymodule_make_link('Magical modal');
}

When user clicks on the link, it makes request either to /mymodule/ajax or /mymodule/nojs (in case of nojs), so the following callback handles creating a modal:

/**
 * Ajax menu callback.
 */
function mymodule_callback($ajax) {
  if ($ajax) {
    ctools_include('ajax');
    ctools_include('modal');

    $form_state = array(
      'ajax' => TRUE,
      'title' => t('MyModule Modal Form'),
    );

    // Use ctools to generate ajax instructions for the browser to create
    // a form in a modal popup.
    $output = ctools_modal_form_wrapper('mymodule_form', $form_state);

    // If the form has been submitted, there may be additional instructions
    // such as dismissing the modal popup.
    if (!empty($form_state['ajax_commands'])) {
      $output = $form_state['ajax_commands'];
    }

    // Return the ajax instructions to the browser via ajax_render().
    print ajax_render($output);
    drupal_exit();
  }
  else {
    return drupal_get_form('mymodule_form');
  }
} 

Now you just need to create a form and its submit handler as below:

/**
 * Drupal form to be put in a modal.
 */
function mymodule_form($form, $form_state) {
  $form = array();

  $form['new_link_text'] = array(
    '#type' => 'textfield',
    '#title' => t('Link text'),
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );

  return $form;
}

/**
 * Drupal form submit handler.
 */
function mymodule_form_submit(&$form, &$form_state) {
  // Generate the new link using the submitted text value.
  $link = _mymodule_make_link($form_state['values']['new_link_text']);

  // Tell the browser to close the modal.
  $form_state['ajax_commands'][] = ctools_modal_command_dismiss();

  // Tell the browser to replace the old link with the new one.
  $form_state['ajax_commands'][] = ajax_command_replace('#magical-modal-link', $link);
}

To test that, go to: /mymodule/page where you should see 'Magical Modal' link which should show you the modal form once clicked.


I would start looking at Modal form, instead of Colorbox. It specifically exists because using Colorbox with forms works really bad.

In modal form, Ctools does all the work in background, which has proper support for form handling that just doesn't belong in Colorbox. That also means that if you need to "modal" an unsupported form then you always know that thanks to Ctools there's a solid API behind it that you can switch to instead.

Bonus points for filing a patch with new form support if you write it. ;)

Tags:

Forms

7