Adding additional form elements using ahah_helper in Drupal 6

This tutorial will show you how to add new form elements which will be available in $form_state['values'] once the form has been submitted using a submit button by taking advantage of the AHAH helper module.

The AHAH helper module does provide a demo, but the demo shows how to change a few fields depending on a selection made from a select element. The tutorials I looked at online either didn't cover the issue properly or just didn't work.

Currently the 6.x-2.0 release throws up errors if you are using certain PHP versions, but these are gone in the 6.x-2.x-dev release.

Scenario

You want the user to enter in some data, lets say their favourite car. But you want the ability for them to add as many cars as they want if they have more than one.

Code

Firstly you will need to set up a menu callback for your form, but I will assume you know how to do this if you are looking at this tutorial.

We will set up the basic form first, this includes one textfield and a submit button to submit the form with the textfield in a wrapper (this is important later on for the ahah_helper module).

<?phpfunction test_form($form_state = NULL) {
  $form['cars'] = array(
    '#type' => 'fieldset',
    '#title' => t('Favourite cars(s)'),
    '#prefix' => '<div id="favourite-cars-wrapper">',
    '#suffix' => '</div>',
  );
  $form['cars']['car'] = array(
    '#type' => 'textfield',
    '#title' => t('Favourite car'),
    '#size' => 36,
    '#default_value' => $form_state['values']['car'],
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}?>

This is very basic and allows the user to enter one car and submit the form. We now need to add in the extra functionality of ahah_helper to be able to add mulitple fields.

<?phpfunction test_form($form_state = NULL) {
  
  ahah_helper_register($form, $form_state); // Absolutely vital for ahah_helper to do it's job

  // We check to see if quantity has been set, ie if the form has been submitted yet.  If not, we show 1 field, you can change this to however many fields you want to show up as default.
  if (!isset($form_state['storage']['quantity'])) {
    $quantity = 1;
  }
  else {
    // If the form has been submitted we get the quantity that was stored.
    $quantity = $form_state['storage']['quantity'];
    // We then do a check to see if the user has clicked the "Add another car" button, if they have we increase the amount by 1.
    if (isset($form_state['values']['cars']['add_more']) && $form_state['values']['op'] == 'Add another car') {
      $quantity++;
    }
  }

  // We create a hidden form element to store the amount of fields that the user has added.
  $form['quantity'] = array(
    '#type' => 'value',
    '#value' => $quantity,
  );

  $form['cars'] = array(
    '#type' => 'fieldset',
    '#title' => t('Favourite cars(s)'),
    '#prefix' => '<div id="favourite-cars-wrapper">',
    '#suffix' => '</div>',
    '#tree' => TRUE // This is important for ahah_helper.
  );
  // We now do a simple loop, creating however many textfields are defined by $form_state['storage']['quantity']
  for ($i = 1; $i <= $quantity; $i++) {
    // The element name needs to be different for each textfield, otherwise we will only get one value after the form is submitted.
    $form['cars']['car_'. $i] = array(
      '#type' => 'textfield',
      '#title' => t('Favourite car'),
      '#size' => 36,
      '#default_value' => $form_state['values']['cars']['car_'. $i],
    );
  }

  // We add in a button with the #ahah element which will handle all our work for us.
  $form['cars']['add_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add another car'),
    '#ahah' => array(
      'event' => 'click', // When the button is "clicked", AHAH will do it's job
      'path' => ahah_helper_path(array('cars')), // The array features the wrapper form field. So our form wrapper is $form['cars'], so we set this to array('cars'). If your form was $form['cars']['another_wrapper'], the path would be array('cars', 'another_wrapper').
      'wrapper' => 'favourite-cars-wrapper', // We then define the wrapper which will be changed.
    ),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  return $form;
}?>

So what have we done...first we added the following line:

<?phpahah_helper_register($form, $form_state);?>

This is a vital function that needs to be present in your form for ahah_helper to do it's job.

Next we check to see if the form has been submitted before and if it has, get the amount of textfields that the form had by storing it in a hidden field. We then do a further check to find out what button was clicked. If the "add more" button was clicked, then we add another textfield by increasing the value of the quantity variable.

A minor, but very important change we make is adding '#tree' => TRUE to our wrapper element, this is a requirement for ahah_helper to do it's job properly.

Next we use a for loop, to add all our textfields into the form. This should be self-explanitory but will go in to a bit more depth further on when we talk about the #ahah element.

We added an additional button to the form, this is so that the user can add another field to the form. You don't need to worry about javascript degradation as ahah_helper takes care of this.

One attribute of #ahah on the button that isn't listed in the form, but is very important, is the "method". The default value of this is replace, which is what we want. This replaces the whole contents of the wrapper which we define with the data from the path which we defined. This is why we needed the for loop to recreate the textfields.

To take care of the form when javascript is disabled, you must check which button was pressed when the form was submitted.

<?phpfunction test_form_submit($form, $form_state) {
  if ($form_state['values']['op'] == 'Submit') {
    drupal_set_message('Submit button clicked');
  }
  else {
    drupal_set_message('Add more button clicked');
  }
}?>

With javascript enabled, when clicking the Add more button, Drupal will perform validation on the form, but will not submit the form. Whereas clicking the submit button will perform validation and submit the form.

Comments

[...] This post was mentioned on Twitter by web dev fools. web dev fools said: James Tombs: Adding additional form elements using ahah_helper in Drupal 6 http://bit.ly/c2Ebjv #drupal [...]

thanks for this tutorial its really helpfull

Thanks so much for this James. Really useful.

Thanks! Very helpful. (Maybe need to add "return $form;" at the end of the function) ?

Ah yes, thanks for pointing that out. That is the problem when you don't copy and paste.

hi,

Your site is so helpful - do you know if there's a way to avoid validating the form until submit using the ahah_helper module?

thanks!

I don't although I would like to know that to as it would be useful for something I am working on at the moment.

thanks, I'll check back on your site.

another question, if I may - I tried implementing your logic in my exiting code (undoing all of the Drupal 6 AHAH and retrofitting it),
and I am unable to get the display to refresh with the additional form elements, including the counter which I'm displaying as a visual check.

however, when I finally hit the submit button, the correct number total of elements is detected, so the incrementation has been taking place, just not displayed.

would you have any ideas that come readily to mind (without inspecting the code)?

thanks again!

Not sure, can you post the code in something like http://pastie.org/

http://pastie.org/1120530

meanwhile, I am going to place your sample code into my module and try to slowly morph it into the desired functionality, so I can figure out where the problem is.

I can't see anything clearly wrong with it.

Add this bit of code to your function (http://pastie.org/1120811) that will give you the structured form_state array so you can see what values are being updated each time the form is submitted.

thanks for looking - I will add the trace code and hope to figure it out.

hey can you tell me where these functions go...
Is there 3 one for the form and another for form to loop and a submit iam still way new to this...

Thanks ahead of time...

sorry I don't think I asked the right ?.

Is this all one function besides the submit?

I have no idea on how to use ahah events/elements or whatever.

There are 2 functions which I have listed. The form and the submit function.

Thanks for the fast reply, so your first part is really all wrapped up as one and then the submit. So this should add a text field to the form or just tell me the message?

The first bit of code is a simple form with a textfield and a submit button. The second bit of code is the same but with the AHAH code to have a textfield and an add more button as well as a submit button. The final piece of code is the submit handler for it where you can check to see what button was clicked.

I see that, What I am having trouble with is it will only let me use it once then in firebug it throws the error:

$(element_settings.element).parents("form").ajaxSubmit is not a function

only on the second submit.

How can I have it show the posted element "car" and have it show the field. I am just really confused on how it all works. I have been reading the doc on it and the real ahah but seem to lack something. sorry for the lame questions over and over.

so is the "function test_form($form_state = NULL) {" the same on both of these top 2 functions?

Thanks again

Not on submit but the add another car... sorry about that

Yes they are the same, the second one is the first one with the required code, so you can forget about the first one.

The problem you are having sounds like an error which shouldn't be happening.

James- Thank you for the great tutorial, this is the best example of adding elements with ahah on the web. I have been able to get the example working fine, but now I am trying to save the added elements to my database on the form submit. I am using the scaffolding example for record save. How is this done? I have had no luckā€¦ I am saving value like this $record = $form_state['values']; and these are the field I created in my .install schema file 'other_consultants_name_' => array(
'type' => 'varchar',
'length' => 255,
'description' => t('Other consultants name'),
),
'eid_dealercode_' => array(
'type' => 'varchar',
'length' => 255,
'description' => t('EID/Dealercode'),
),
I have used dpm($form_state); and the value are showing up named correctly
'other_consultants_name_1
'other_consultants_name_2
etc
Any tips for doing this, thanks for any help you can provide

You will need to create an array of your different values (other_consultants_name_1, other_consultants_name_2 etc) and loop through that array saving each individual record.

Did anyone try to use ahah_helper with CCK 3.dev. I tried to bind ahah to custom fields with no success. I also receive error because cck multigroup also uses ahah and something is messing with wrapper of multifield. Any topics that could help?

what if i have to develop the same code in drupal5. am using js to add new row when add button is clicked. but the values are not been submitted by the form.any solution for this?

No, idea. You would have to look through the AHAH module and try and implement it in D6.

Thanks, james! your article is very helpfull! you saved me! thanks!

Thank you for a great tutorial that saved me so much time.