Ahah forms in drupal 6

Writing ahah forms in drupal 6 is very easy.   I used it in a project where a custom slide node form had to be updated by ajax, depending on the Microsoft Exchange Server Clé selected slide_template. The slide_template object is built into the node object as composite. This way I can approach the form as $node->template->has_new_tag() , and this way update the form with fields needed for that specific template.

The fapi is pretty straight forward but I encountered a slight problem when trying to build the extendable form like I thought it was gonna be.  Calling the drupal_render function did not populate the id and name field of the elements.  Some quick peeking into the common and form functions in Drupal, I found that drupal was not notified of the the ajax-added field. The trick is to get the cached form and add the fields into the form which drupal already stored.

Calling drupal_render just isn’t enough. Microsoft Storage Server 2008 Clé I have to say that I googled a lot to find the cleanest way to fix this as it was difficult to discribe. I found two nice articles on this: drupal 6 AHAH forms: the easy way by Nick Lewis and AHAH helper module by Wim Leers.

The ahah_render way

As i had already a common file into my project, i tried the ahah_render function. This made sense because it actually does exactly what I wanted it to do. Make the added fields known in the node form so Drupal can process them.  This way the name is set and it is posted within the node.

  1. function ahah_render($fields, $name) {
  2.   $form_state = array(’submitted’ => FALSE);
  3.   $form_build_id = $_POST[‘form_build_id’];
  4.   // Add the new element to the stored form. Without adding
  5.   // the element to the form, Drupal is not aware of this new
  6.   // elements existence and will not  process it. We retreive
  7.   // the cached form, add the element, and resave.
  8.   $form = form_get_cache($form_build_id, $form_state);
  9.   $form[$name] = $fields;
  10.   form_set_cache($form_build_id, $form, $form_state);
  11.   $form += array(
  12.     ‘#post’ => $_POST,
  13.     ‘#programmed’ => FALSE,
  14.   );
  15.   // Rebuild the form.
  16.   $form = form_builder($_POST[‘form_id’], $form, $form_state);
  17.   // Render the new output.
  18.   $new_form = $form[$name];
  19.   return drupal_render($new_form);
  20. }

In my code then I build the slide object with a slideFactory giving the template as parameter. This way the slide object (subclass of the abstract slide class) knows what to do when the get_form method is called. It returns a set of elements, rendered by the nice ahah_render function populating the div from the ahah['#wrapper'] property.

  1. /**
  2.  * Gets a template form
  3.  */
  4. function slide_template_get_form($temp, $slideshow_id) {
  5.   $template = slide_get_template($temp);
  6.   if (!empty ($template->template_name)) {
  7.     require (dirname(__FILE__) . ‘/slide.class.php’);
  8.     $slide = SlideFactory :: create_slide($template);
  9.     $slideshow = node_load($slideshow_id);
  10.     $slide->set_container($slideshow);
  11.     $form = $slide->get_form();
  12.     // Calls drupal_render with knowledge of the current node form
  13.     return ahah_render($form, ’slide_form’);
  14.   } else {
  15.     return
  16. <div class="messages error">Error, template ‘ . dsm($template) . ‘ does not exist</div>
  17. ;
  18.   }
  19. }

The callback function that calls the slide_template_get_form function:

  1. /**
  2.  * Callback for to load slide form
  3.  */
  4. function slide_callback_form_js() {
  5.   $message = ;
  6.   if (empty ($_POST[‘template_id’])) {
  7.     drupal_json(array (’status’ => FALSE,‘data’ => ‘No slide template selected.’));
  8.   }
  9.   if (empty($_POST[’slideshow_id’])) {
  10.     drupal_json(array (’status’ => FALSE,‘data’ => ‘No slidehow given.’));
  11.   }
  12.   $template_id = $_POST[‘template_id’];
  13.   $slideshow_id = $_POST[’slideshow_id’];
  14.   $message .= slide_template_get_form($template_id, $slideshow_id);
  15.   drupal_json(array (’status’ => TRUE,‘data’ => $message));
  16. }

In the module itself the hook_form could look like this:

  1.   $form[’slide_form’] = array (
  2.     ‘#type’ => ‘markup’,
  3.     ‘#value’ => ‘<!– This will be replace by the form –>’,
  4.     ‘#prefix’ => ‘<div id="slide_form">’,
  5.     ‘#suffix’ => ‘</div>’,
  6. );

Ahah_helper module

The ahah helper module provides a neat function ‘ahah_helper_path’ to let drupal know about new field elements. This way you can set the path “magically” and the helper module takes care of how the new fields will be rendered. You can use it like this:

  1. $form[’slide_form’][‘new_field’] = array
  2.   // field properties …

The ahah property path will be:

  1.  ‘#ahah’ = array (
  2.   ‘path’ => ahah_helper_path(array(’slide_form’, ‘new_field’)),
  3.   // other properties
  4. );

This helper module makes it very easy to update a form with your own ajax-driven field addtions. It helps memorizing the form so your new fields are known by drupal. At the form api reference link, we can see which fields can trigger this behavior: button, checkbox, image button, password, radio, select, submit, textarea, textfield.
An extra advantage of using the ahah helper module is that when javascript is disabled, the same function will be called with a normal post and this will work just fine.  Sometimes it can be helpfull to check #first_time parameters in callbacks to see if the data was previously saved.

I hope this helped your dynamic ajax forms creations or even helped your start off with ajax driven forms. At least you see there is an easy way of doing things the ajax or ahah way.

This entry was posted on Saturday, September 27th, 2008 at 4:57 pm and is filed under Drupal. You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.

5 Responses to “Ahah forms in drupal 6”

  1. Ahah forms in drupal 6 Says:

    [...] Go to the author’s original blog: Ahah forms in drupal 6 [...]

  2. Wim Leers Says:

    What exactly did you have to “tune up a little”? Please let me know in the AHAH helper issue queue!

  3. Stalski Says:

    @Wim: With ahah_helper, you can’t set wildcards but I guess who needs that if you can send hidden fields to route the form. I had to change a lot of code from a callback function to the normal hook_form.
    By checking a $form_state['values']['my_former_wildcard_num'] I did the same thing. So it is possible to do things without wildcards in menu callbacks.
    So it isn’t really an issue …

  4. Rich Thomas Says:

    I know this is kind of out of the blue but we just wrote an informative post on “Drupal Ajax Form Validation” over at DrupalConnect.com. You can view the post here - http://www.drupalconnect.com/blog/jwhitener/drupal-ajax-form-validation . I was searching the web a bit and came across your site & think this would be a perfect post for you to share with your readers.
    We are trying to increase our readership so if you want to give us a shout-out to your readers, we would be more than appreciative. If you ever have great posts for us, by all-means please send them my way.
    We love meeting other Drupal Fanatics…
    Cheers,
    Rich

  5. Behzad Ravanbakhsh Says:

    There are many problems and issues on using ahah and ahah_helper modules such as problem with existing file upload fields in the form or hierarchical select form elements. Fortunately some issues were solve yet.

    for fix them visit
    http://drupal.org/project/issues/ahah_helper