Menu

Insert a form into a pop-up modal with CTools and Drupal 7

John Ennew

Aug 12 2013

This tutorial shows you how to insert a form into a modal so it pops up on your site after a user clicks a link, without having to write any JavaScript at all. The functionality that supplies this is the CTools module and core ajax functionality in Drupal 7. In this tutorial, we will create a page which has a single link on it. When clicked, a pop-up modal will show a form with a text box and submit button. When submitted, the modal will close and the text in text box will replace the text in the link.

Labs modal example

1. Create a custom module

For this tutorial we will create a custom module to demonstrate the code. Let's call it mymodule and create a folder for it in sites/all/modules. The first file to create is mymodule.info with the following code:

 name = My Module
 core = 7.x
 dependencies[]=ctools
,

2. Define the routing paths in hook_menu

Next create a mymodule.module file in the folder as well. We will declare two endpoints via hook menu:- 1. "/mymodule/page" - A page that a user might travel to and see the link that, when clicked, will cause a modal to popup with the form in it. 2. "/mymodule/%ctools_js" - The second is a callback which returns the HTML of the modal form. The second argument is a variable. It's value will be either "nojs", if the users browser does not handle JavaScript, or "ajax" if it does.

 /**
 * Implements hook_menu().
 */
function mymodule_menu() {
 $items = array();

 $items['mymodule/page'] = array(
 'page callback' => 'mymodule_page',
 'access callback' => TRUE,
 'type' => MENU_CALLBACK,
 );

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

 return $items;
}

2. Define the routing paths in hook_menu

Next create a mymodule.module file in the folder as well. We will declare two endpoints via hook menu:- 1. "/mymodule/page" - A page that a user might travel to and see the link that, when clicked, will cause a modal to popup with the form in it. 2. "/mymodule/%ctools_js" - The second is a callback which returns the HTML of the modal form. The second argument is a variable. It's value will be either "nojs", if the users browser does not handle JavaScript, or "ajax" if it does.

 /**
 * Implements hook_menu().
 */
function mymodule_menu() {
 $items = array();

 $items['mymodule/page'] = array(
 'page callback' => 'mymodule_page',
 'access callback' => TRUE,
 'type' => MENU_CALLBACK,
 );

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

 return $items;
}

,

3. Create a link generator

This small piece of code is a helper function for generating the link. It takes one argument which is the text of the link and returns the HTML. It is important for ctools and the ajax subsystem in Drupal to work that the URL in the ajax link includes a nojs component and has a class of ctools-use-modal

 /**
 * 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>';
}

3. Create a link generator

This small piece of code is a helper function for generating the link. It takes one argument which is the text of the link and returns the HTML. It is important for ctools and the ajax subsystem in Drupal to work that the URL in the ajax link includes a nojs component and has a class of ctools-use-modal

 /**
 * 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>';
}
,

4. Define the page callback

This is the page the user will go to and see the link. This page should include the ctools modal JavaScript libraries.

 /**
 * 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');
}

4. Define the page callback

This is the page the user will go to and see the link. This page should include the ctools modal JavaScript libraries.

 /**
 * 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');
}
,

5. Define the ajax callback

This is what runs when the user's browser makes a request to either "/mymodule/nojs" or "/mymodule/ajax". In the case of nojs, the full form must be returned as normal. In the case of "ajax", the ajax delivery system sends back the information on 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');
 }
}

5. Define the ajax callback

This is what runs when the user's browser makes a request to either "/mymodule/nojs" or "/mymodule/ajax". In the case of nojs, the full form must be returned as normal. In the case of "ajax", the ajax delivery system sends back the information on 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');
 }
}
,

6. Create an example form

The next thing to do for this example is to create the form and its submission function. In 5 above, we told ctools the forms name was "mymodule_form", so lets create that form now with a textfield and a submit button. You'll note that there is nothing special about the form itself and any Drupal form could therefore be used.

 /**
 * 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;
}

6. Create an example form

The next thing to do for this example is to create the form and its submission function. In 5 above, we told ctools the forms name was "mymodule_form", so lets create that form now with a textfield and a submit button. You'll note that there is nothing special about the form itself and any Drupal form could therefore be used.

 /**
 * 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;
}
,

7. Create the submission function

When submitting the form we want to send back ajax commands to the browser to dismiss the modal and change the text on the link we clicked. You can see the ajax_command_replace function is targeting the div with the id set by our helper function back in 3. In situations where you are not using your own form but some other in the Drupal system you might need to add additional ajax actions by including your own submission functions to the 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);
}

7. Create the submission function

When submitting the form we want to send back ajax commands to the browser to dismiss the modal and change the text on the link we clicked. You can see the ajax_command_replace function is targeting the div with the id set by our helper function back in 3. In situations where you are not using your own form but some other in the Drupal system you might need to add additional ajax actions by including your own submission functions to the 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);
}
,

8. Test it out

Now enable the mymodule module and point your browser to "/mymodule/page". You should see a link with the words "Magical Modal". Clicking the link should open the form and submitting the form should change the text of the link.

John Ennew

About the author

John Ennew

Technical Director