Drupal 6: Using AHAH to dynamically generate form elements (and integration with multi-tiered taxonomy)
For some time now I’ve wanted to write a blog entry about using AHAH to create dynamically generated form elements. After a recent conversation at work regarding usability, I now had a real world example to create: how to use tiered taxonomy to dynamically generate a form. This code snippet will show you how to create a form that creates child select dropdowns based on the parent taxonomy term the user selects.
First I established a multi-tier taxonomy called “AHAH”:
For this example I created a menu callback to display my initial form:
<?php
function helper_menu() {
$items = array();
$items['ahah-form'] = array(
'title' => 'AHAH Form',
'page callback' => 'drupal_get_form',
'page arguments' => array('_helper_callback_ahah_form'),
'type' => MENU_CALLBACK,
'access callback' => 'user_access',
'access arguments' => array('access content'),
);
return $items;
}
?>
I then defined the page callback to show the initial form:
<?php
function _helper_callback_ahah_form() {
// define an array to contain form elements
$form = array();
// define the top level vid
$vid = 2;
// fetch a tree of taxonomy elements
$tree = taxonomy_get_tree($vid, 0, -1, 1);
// loop though taxonomy and collect elements
$options = array();
foreach ($tree as $key => $value) {
$options[$value->tid] = $value->name;
}
// create the first select dropdown input
$form['select_1'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t('Select 1'),
'#size' => 5,
'#multiple' => false,
'#ahah' => array(
'event' => 'change',
'path' => 'ahah-form-callback',
'wrapper' => 'wrapper-1',
'method' => 'replace',
),
);
// pass the top level vid in the form
$form['ahah_vid'] = array(
'#type' => 'hidden',
'#value' => $vid,
);
// create an empty form element to contain the second taxonomy dropdown
$form['wrapper_1'] = array(
'#prefix' => '<div id="wrapper-1">',
'#suffix' => '</div>',
'#value' => ' ',
);
// add a form submit button
$form['submit'] = array(
'#value' => 'Submit',
'#type' => 'submit'
);
return $form;
}
?>
The above form callback produces the following:
Next I defined a callback to handle the AHAH page request:
<?php
// new menu item:
function helper_menu() {
// ...
$items['ahah-form-callback'] = array(
'title' => 'AHAH Form Callback',
'page callback' => '_helper_callback_ahah_form_callback',
'type' => MENU_CALLBACK,
'access callback' => 'user_access',
'access arguments' => array('access content'),
);
// ...
return $items;
}
// and, here's the AHAH callback used to create the new form elements:
function _helper_callback_ahah_form_callback() {
// define a string variable to contain callback output
$output = "";
// pull the top level vid from the $_POST data
$vid = $_POST['ahah_vid'];
// pull the selected dropdown from the $_PODT data
$parentVid = $_POST['select_1'];
// loop through the taxonomy tree and fetch child taxonomies
$options = array();
$tree = taxonomy_get_tree($vid, $parentVid, -1, 1);
foreach ($tree as $key => $value) {
$options[$value->tid] = $value->name;
}
// define the second tier select dropdown element
$form['select_2'] = array(
'#type' => 'select',
'#options' => $options,
'#title' => t('Select 2'),
'#size' => 5,
'#multiple' => false,
);
// rebuild form object and output new form elements
$output .= ahah_render($form, 'select_2');
// render form output as JSON
print drupal_to_js(array('data' => $output, 'status' => true));
// exit to avoid rendering the theme layer
exit();
}
// Lastly, here's a help function pulled from Nick Lewis's blog to alter the form
// see: http://www.nicklewis.org/node/967
// NOTE: based on poll module, see: poll_choice_js() function in poll.module
function ahah_render($fields, $name) {
$form_state = array('submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
// Add the new element to the stored form. Without adding the element to the
// form, Drupal is not aware of this new elements existence and will not
// process it. We retreive the cached form, add the element, and resave.
$form = form_get_cache($form_build_id, $form_state);
$form[$name] = $fields;
form_set_cache($form_build_id, $form, $form_state);
$form += array(
'#post' => $_POST,
'#programmed' => FALSE,
);
// Rebuild the form.
$form = form_builder($_POST['form_id'], $form, $form_state);
// Render the new output.
$new_form = $form[$name];
return drupal_render($new_form);
}
?>
The above code allows the user to select an option from the top level tier of taxonomy and the AHAH callback will generate the a select dropdown of the child taxonomies as shown below:
On form submission, you’ll see that the options the user selected as stored in $form_state[‘values’][‘select_1’] and $form_state[‘values’][‘select_2’]