Drupal 6 AHAH forms: The easy way

Today, I was working with drupal 6's AHAH form elements. Initially, I was delighted at how well they worked. That delight turned to confusion once I realized that the form elements I had put in the menu callback of the #ahah['path'] was missing its name attribute. After doing a bit of research in how the poll module handled the formapi voodoo, I created a generalized function to aid in building AHAH callbacks. If there is a better way to do this, I wasn't able to find it.

<?php
// this is an example menu_callback that would be referenced by the #ahah['path'] property
function easy_ahah_form_field() {
 
// all you have to worry about is the new form field that will be inserted via #ahah
 
$form = array(
   
'#type' => 'select',
   
'#title' => 'You selected that because...',
   
'#options' => array(
     
'1' => 'drugs',
     
'2' => 'I do what I want.',
     
'3' => "I'm feeling lucky..."
   
),
   
  );
 
// ahah_render is where the magic happens.
  // 'the value of this field will show up as $form_value['user_problem']
 
$output = ahah_render($form, 'user_problem');
  print
drupal_to_js(array('data' => $output, 'status' => true));
  exit();
}
/*
  This function is largely based on the poll module, its been simplified for reuse.
  $fields is the specific form elements you want to attach via ahah,
  $name is the form fields array key... e.g. the name for $form['title'] is "title"
*/
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); 
}
?>

Comments

Maybe I'm missing something...

OK, maybe I'm missing something, but when I use this code it replaces my existing form with the new select element, rather than appending the new element to the form. Can anyone suggest what am I doing wrong? My code is based heavily off the poll module, as well. Many thanx for the help, Ben

Be sure to set your

Be sure to set your $form['#ahah']['wrapper'] to the css ID you wish to replace.

thanks

i'm not sure if i got it quite right, but if so thanks for the post. it might be quite useful.

Thank you very much!

I've been similarly elated and then disappointed/confused by AHAH fields in D6. This may help enormously.

Is there something similar,

Is there something similar, but for drupal 5 ?!

No. And don't quote me on

No. And don't quote me on this, but I think the formapi needed to be tweaked in 6 to allow AHAH to work too. So backporting it to 5 is probably not practical.

Could be improved in D7

This is certainly something I want to get improved before D7 rolls out. For one, I want to eliminate the #ahah['path'] property and put in a #ahah['callback'], where you can call a function directly without making a menu callback. Second, there *really* needs to be an easier way to render just a portion of a form. Even though the JavaScript side of things is really easy, we need better support on the FormsAPI side to render a small piece of the form.

Thanks for putting together this small example, hopefully it won't be needed forever. :)

Impractical

I want to eliminate the #ahah['path'] property and put in a #ahah['callback'], where you can call a function directly without making a menu callback.

You need a menu callback at some point along the line - whether or not this menu callback is a single one implemented by the Form API and invisible to your module. As long as you have to go through the menu system, you might as well do it directly...