AHAH - populating a select (list) with a select (drop-down)
Predated - March 23, 2009 - 15:22
Issue solved, please see the 3rd comment below for an example of making it all work.
Thanks to Wim Leers and Kat Bailey, and alot of sites that were talking about AHAH. I also thank my brain for staying in my head while I beat it into submission around the whole concept of AHAH in Drupal 6.

Your form must react to $form_state
You need to first of all make sure that your form will react to $form_state, i.e. that it will always get built according to what's in $form_state. What this means in your particular case is that in your form function when you are building your rooms dropdown, you first check to see if you have a selected location id and populate it based on that. So you'll call a function to look up the rooms for the selected location and that's where your SQL query will go, NOT in your ahah callback. That way, when your ahah callback renders the form, it'll do so with an actual value for lid, which your rooms dropdown will then react to. The Quick Tabs module does this for its View displays dropdown so you could have a look there to get a better understanding of how it works.
Thanks Kat :) I actually
Thanks Kat :)
I actually found your blog post about the updates you made in Quicktabs where you discussed all that, I just hadn't posted back about it. I'm pretty sure I have it hammered out at this point, but another project I was working on got it's priority bumped up, and I haven't had a chance to get back to it.
I think I was making AHAH a lot more complicated than it is, and I was getting really frustrated with it. Going back to it after not looking at it for a couple of days made quite a difference. I'll have to post back here when I have it working.
Got it working
I got it working some time ago, and just never got around to posting it back here. But in the interest of saving someone some frustration down the road, there is an example of the bare minimum you'd need down below. Please note that the example code hasn't been tested.
You'll want to have a look at:
Doing AHAH in D6 the right way
Wim Leers' blog post about ahah_helper
Kat Bailey's blog post on the duality of forms
First of all you'll need a menu callback:
<?phpfunction simple_menu() {
$items['simple/js'] = array(
'type' => MENU_CALBACK,
'page callback' => 'simple_ahah_render',
'access callback' => TRUE,
);
return $items;
}
?>
And of course, you'll need a form:
<?php
function simple_form($form_state) {
$form['content'] = array(
'#tree' => TRUE,
'#prefix' => '<div id="simple-select-wrapper">',
'#suffix' => '</div>',
);
$form['content']['simple_select_parent'] = array(
'#type' => 'select',
'#default_value' => $form_state['storage']['category']['select_cat'],
'#options' => simple_get_options(), // Get some options for this select from the DB
'#ahah' => array(
'path' => 'simple/js',
'wrapper' => 'simple-select-wrapper',
),
);
// Here we react to $form_state, adding content to the form, depending on the
// submitted value of simple_select_parent, above.
if ($form_state['storage']['content']['simple_select_parent']) {
$form['content']['simple_select_child'] = simple_ahah_select($form_state);
}
return $form;
}
?>
And you'll need to get the options for your select field somehow as well - this function would pull it from a database:
<?php
/**
* Return a FAPI select element whose options are based on values submitted by a
* separate select field.
*
* @param array $form_state
* The submitted values of a form. We're mostly interested in $form_state['storage']
* which is where all the pertinent information will be.
* @return array $element
*/
function simple_ahah_select($form_state) {
$sql = "SELECT id, name FROM {your_db_table} WHERE id = %d";
if ($form_state['storage']['content']['simple_select_parent']) {
$result = db_query($sql, $form_state['storage']['content']['simple_select_parent']);
while (($data = db_fetch_object($result)) !== FALSE) {
$opt[$data->id] = $data->name;
}
}
$element = array(
'#type' => 'select',
'#options' => $opt,
);
return $element;
}
?>
The magic all happens with your AHAH callback function, in this case, simple_ahah_render:
<?php
function simple_ahah_render() {
$form_state = array('storage' => NULL, 'submitted' => FALSE);
$form_build_id = $_POST['form_build_id'];
$form = form_get_cache($form_build_id, $form_state);
$args = $form['#parameters'];
$form_id = array_shift($args);
$form['#post'] = $_POST;
$form['#redirect'] = FALSE;
$form['#programmed'] = FALSE;
$form_state['post'] = $_POST;
// Prevents _form_builder_ie_cleanup() from incorrectly assigning the
// first button in the form as the clicked button.
// Wim Leers' AHAH Helper Module has more in-depth information.
// @see the ahah_helper project
$form_state['submitted'] = TRUE;
drupal_process_form($form_id, $form, $form_state);
// Once drupal_rebuild_form is called, we no longer have access to
// $form_state['values'], so we merge it into $form_state['storage'].
if (isset($form_state['values'])) {
if (!isset($form_state['storage'])) {
$form_state['storage'] = array();
}
$storage = $form_state['storage'];
$values = $form_state['values'];
$form_state['storage'] = array_smart_merge($storage, $values);
}
// Rebuild the form.
$form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);
// IMPORTANT:
// This is a simple example, and we know what the new form element is called. In a
// real world scenario, you would need to pass a callback function to this AHAH
// render function that would return the new content, or get the new content in
// some other way - this is just a simple example.
$output = $form['content']['simple_select_child'];
// Get the JS settings so we can merge them.
$javascript = drupal_add_js(NULL, NULL, 'header');
$settings = call_user_func_array('array_merge_recursive', $javascript['setting']);
drupal_json(array(
'status' => TRUE,
'data' => theme('status_messages') . drupal_render($output),
'settings' => array('ahah' => $settings['ahah']),
));
}
/**
* Smarter version of array_merge_recursive: overwrites scalar values.
*
* This also came (like a God send) from Wim's AHAH helper module. Really, that's the
* easiest way to go, and the module works like a charm - but I wanted to get my
* head around the whole AHAH thing, and maybe you do to, or maybe you can't or don't
* want to be dependant on a different module.
*
* @see PHP Manual on: array-merge-recursive comment #82976.
*/
function array_smart_merge($array, $override) {
if (is_array($array) && is_array($override)) {
foreach ($override as $k => $v) {
if (isset($array[$k]) && is_array($v) && is_array($array[$k])) {
$array[$k] = array_smart_merge($array[$k], $v);
}
else {
$array[$k] = $v;
}
}
}
return $array;
}
?>
I am still a little bit confused
I got it to work,
forget about my last message
thanks!
I think my brain did explode, but thanks a ton for writing this up!! Finally I'm not getting "Drupal detected an illegal action" errors.
Tom Wheeler
http://www.wheelercreek.com
Thank you
Thank you for this
SkinnyBarriers Web Design and Systems Development