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

katbailey - March 25, 2009 - 22:45

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

Predated - March 29, 2009 - 02:46

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

Predated - May 21, 2009 - 15:56

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:

<?php
function 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

javier.ortiz.llerena - July 24, 2009 - 15:26

I got it to work,
forget about my last message

Hi,
first of all thank you for helping with your work and sharing with the drupalers,
but for those like me that are more newbies in the durpal world would be very
useful if you could post the entire code of a working example.

I have been looking at this for two days and I couldn't make it work.
Sorry about that.

thanks!

wheelercreek - November 11, 2009 - 19:28

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.

Thank you

sicnic - September 11, 2009 - 14:37

Thank you for this

SkinnyBarriers Web Design and Systems Development

 
 

Drupal is a registered trademark of Dries Buytaert.