AHAH Changes for Drupal 7 (It's now called #AJAX)

Drupal 7 AHAH has changed its name to AJAX to use a more familiar term and to be slightly more trendy. A version of this presentation for D7 AJAX is at http://randyfay.com/ajax. D7 AJAX handling is massively simplified.

The AJAX Example for D7 is now in the Examples Module and will be maintained there. If you find AJAX features that you'd like to see in it that aren't there, file a request or submit a patch.

30 Seconds on the Drupal Form API

It's pretty easy to forget all the details of the Drupal Forms API, so we'll stop for a minute for a very short review.

(This one is at ahah_demo/simple_form.)

You add a form to your page like this:

$output .= drupal_get_form('ahah_demo_simplest_form');
?>

Often that's done right from the hook_menu implementation.

The layout of a form is as below:

/**
* A Hello-world for Forms API (FAPI)
*/
function ajax_demo_simplest_form($form, &$form_state) {

$form['explanation'] = array(
'#type' => 'markup',
'#value' => '

' . t('This is a basic form with just a bit of information') . '

',
);

$form['experience'] = array(
'#type' => 'select',
'#title' => t('What is your experience level in Drupal?'),
'#options' => array(
'expert' => t('Expert'),
'journeyman' => t('Journeyman'),
'beginner' => t('Beginner')),
);
$form['more'] = array(
'#type' => 'textfield',
'#title' => t("Say more about yourself"),
'#description' => t('Surely one word can\'t describe you.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Click Me'),
);

return $form;
}

function ajax_demo_simplest_form_submit($form, &$form_state) {
drupal_set_message(t('You have claimed to be %experience: %more',
array(
'%experience'=>$form_state['values']['experience'],
'%more' => $form_state['values']['more'],
)
));
}
?>

AJAX Simplest Example

The essence of AJAX forms is that you mark an element of a form as the one that activates AJAX behavior, and you tell it what section of the HTML is to be replaced when it's activated.

The form we're using here has a textfield with a description, which is just plain HTML. It has a prefix and suffix that mention its CSS ID. The CSS ID will be used by the javascript running in the background to determine what's to be replaced.

$form['changethis'] is marked as AJAX-enabled with the "#ajax" marker, which tells the Form API its callback function and the HTML ID (wrapper) to replace.

When $form['changethis'] is changed, javascript provided by Drupal makes a request to the server, which rebuilds the form and calls the callback, which selects a piece of the form. The resulting form piece is rendered and returned to the page, and is replaced by the javascript that made the original call.

(This one is live at http://d7.drupalexamples.info/examples/ajax_example/simplest and the code is on api.drupal.org at http://api.drupal.org/api/function/ajax_example_simplest/7)

Here's an example of the very simplest AJAX behavior from the Examples module.

/**
* Simple form whose ajax-enabled 'changethis' member causes a text change
* in the description of the 'replace_textfield' member.
*/
function ajax_example_simplest($form, &$form_state) {
$form = array();
$form['changethis'] = array(
'#title' => t("Choose something and explain why"),
'#type' => 'select',
'#options' => array(
'one' => 'one',
'two' => 'two',
'three' => 'three',
),
'#ajax' => array(
'callback' => 'ajax_example_simplest_callback',
'wrapper' => 'replace_textfield_div',
),
);

// This entire form element will be replaced with an updated value.
// However, it has to have the prefix/suffix to work right, as the entire
// div is replaced.
// In this example, the description is dynamically updated during form
// rebuild.

$form['replace_textfield'] = array(
'#type' => 'textfield',
'#title' => t("Why"),
'#prefix' => '

',
'#suffix' => '

',
);

if (!empty($form_state['values']['changethis'])) {
$form['replace_textfield']['#description'] = t("Say why you chose") . " '{$form_state['values']['changethis']}'";
}
return $form;
}
/**
* Callback for ajax_example_simplest.
*
* The form item 'replace_textfield' has already been processed and changed
* when the form was submitted and rebuilt during the AJAX call, so now
* we just return the piece of the form that changed.
*/
function ajax_example_simplest_callback($form, $form_state) {
// The form has already been submitted and updated. We can return the replaced
// item as it is.
return $form['replace_textfield'];
}

?>

AJAX Example: Select control and generated checkboxes

In this example we use a select control to determine how many checkboxes are generated.

(This one is live at http://d7.drupalexamples.info/examples/ajax_example/autocheckboxes and the code on api.drupal.org at http://api.drupal.org/api/function/ajax_example_autocheckboxes/7)

/**
* AJAX-enabled select element causes replacement of a set of checkboxes
* based on the selection.
*/
function ajax_example_autocheckboxes($form, &$form_state) {

$default = !empty($form_state['values']['howmany']) ? $form_state['values']['howmany'] : 1;

$form['howmany_select'] = array(
'#title' => t('How many checkboxes do you want?'),
'#type' => 'select',
'#options' => array(1 => 1, 2 => 2, 3 => 3, 4 => 4),
'#default_value' => $default,
'#ajax' => array(
'callback' => 'ajax_example_autocheckboxes_callback',
'wrapper' => 'checkboxes-div',
'method' => 'replace',
'effect' => 'fade',
),

);

$form['checkboxes_fieldset'] = array(
'#title' => t("Generated Checkboxes"),
// The prefix/suffix provide the div that we're replacing, named by
// #ajax['wrapper'] above.
'#prefix' => '

',
'#suffix' => '

',
'#type' => 'fieldset',
'#description' => t('This is where we get automatically generated checkboxes'),
);

$num_checkboxes = !empty($form_state['values']['howmany_select']) ? $form_state['values']['howmany_select'] : 1;
for ($i=1; $i<=$num_checkboxes; $i++) {
$form['checkboxes_fieldset']["checkbox$i"] = array(
'#type' => 'checkbox',
'#title' => "Checkbox $i",
);
}

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

return $form;
}

/**
* Callback element needs only select the portion of the form to be updated.
* Since #ajax['callback'] return can be HTML or a renderable array (or an
* array of commands), we can just return a piece of the form.
*/
function ajax_example_autocheckboxes_callback($form, $form_state) {
return $form['checkboxes_fieldset'];
}
?>

AJAX Form Resources

AJAX Example: Textfields driven by checkboxes

In this example, we just use checkboxes to determine whether textboxes are displayed.

One of the fundamental ideas of having a form change based on selections within the form is that the form is reconfiguring itself based on $form_state. So here, the generation of the form is driven by $form_state['values']. If the checkbox for last name is checked, then we generate a textfield for last name.

(Experiment with this one at http://d7.drupalexamples.info/examples/ajax_example/autotextfields or see the current code at http://api.drupal.org/api/function/ajax_example_autotextfields/7.)

/**
* Show/hide textfields based on AJAX-enabled checkbox clicks.
*/
function ajax_example_autotextfields($form, &$form_state) {

$form['ask_first_name'] = array(
'#type' => 'checkbox',
'#title' => t('Ask me my first name'),
'#ajax' => array(
'callback' => 'ajax_example_autotextfields_callback',
'wrapper' => 'textfields',
'effect' => 'fade',
)
);
$form['ask_last_name'] = array(
'#type' => 'checkbox',
'#title' => t('Ask me my last name'),
'#ajax' => array(
'callback' => 'ajax_example_autotextfields_callback',
'wrapper' => 'textfields',
'effect' => 'fade',

),
);

$form['textfields'] = array(
'#title' => t("Generated text fields for first and last name"),
'#prefix' => '

',
'#suffix' => '

',
'#type' => 'fieldset',
'#description' => t('This is where we put automatically generated textfields'),
);

if (!empty($form_state['values']['ask_first_name']) && $form_state['values']['ask_first_name']) {
$form['textfields']['first_name'] = array(
'#type' => 'textfield',
'#title' => t('First Name'),
);
}
if (!empty($form_state['values']['ask_last_name']) && $form_state['values']['ask_last_name']) {
$form['textfields']['last_name'] = array(
'#type' => 'textfield',
'#title' => t('Last Name'),
);
}

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

return $form;
}

/**
* Selects the piece of the form we want to use as replacement text and returns
* it as a form (renderable array).
*
* @return renderable array (the textfields element)
*/
function ajax_example_autotextfields_callback($form, $form_state) {
return $form['textfields'];
}

?>

Where to find me

Email me, randy at randyfay.com, Find me on ddev.com, Drupal.org and Slack: rfay, Facebook: randyfay, Hobobiker.com: The story of our 2 1/2 year bike trip.

Updated 2025-12-03