1

I'm trying to add an "add more" field to a form in my custom module. I've been looking at the examples module, with no luck.

Here's what I have so far:

$form['#tree'] = TRUE;
  $form['names_fieldset'] = array(
  '#type' => 'fieldset',
  '#title' => t('People coming to the picnic'),
  // Set up the wrapper so that AJAX will be able to replace the fieldset.
  '#prefix' => '<div id="names-fieldset-wrapper">',
  '#suffix' => '</div>',
);

if (empty($form_state['num_names'])) {
  $form_state['num_names'] = 1;
}

for ($i = 0; $i < $form_state['num_names']; $i++) {
  $form['names_fieldset']['name'][$i] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
  );
}

$form['names_fieldset']['add_name'] = array(
  '#type' => 'submit',
  '#value' => t('Add one more'),
  '#submit' => array('spower_add_more_add_one'),
  // See the examples in ajax_example.module for more details on the
  // properties of #ajax.
  '#ajax' => array(
    'callback' => 'spower_add_more_callback',
    'wrapper' => 'names-fieldset-wrapper',
  ),
);

if ($form_state['num_names'] > 1) {
  $form['names_fieldset']['remove_name'] = array(
    '#type' => 'submit',
    '#value' => t('Remove one'),
    '#submit' => array('spower_add_more_remove_one'),
    '#ajax' => array(
      'callback' => 'spower_add_more_callback',
      'wrapper' => 'names-fieldset-wrapper',
    ),
  );
}
function spower_theme($existing, $type, $theme, $path) {
  $base = array(
    'render element' => 'form',
  );

  return array(
    'spower_my_form' => $base + array(
      'template' => 'spower',
    ),
  );
}

function spower_add_more_callback($form, $form_state) {
  return $form['names_fieldset'];
}

/**
 * Submit handler for the "add-one-more" button.
 *
 * Increments the max counter and causes a rebuild.
 */
function spower_add_more_add_one($form, &$form_state) {
  $form_state['num_names']++;
  $form_state['rebuild'] = TRUE;
}

/**
 * Submit handler for the "remove one" button.
 *
 * Decrements the max counter and causes a form rebuild.
 */
function spower_add_more_remove_one($form, &$form_state) {
  if ($form_state['num_names'] > 1) {
    $form_state['num_names']--;
  }
  $form_state['rebuild'] = TRUE;
}

The problem is that when I click "add more", the entire form is submitted.

If the above isn't clear, I've added the module at http://pastebin.com/Hn7bkM2v and the spower_elements.inc at http://pastebin.com/CwHW5mSm.

Thanks for the help.

  • What do you mean by, "the entire form is submitted"? Do you mean the form submission handler is invoked? – kiamlaluno May 19 '13 at 15:55
  • Yes - the form sumission handler – Trevor Newhook May 20 '13 at 0:12
2

It's a well known problem of Drupal 7 - every form button is rendered with type="submit".

What you need to do, is to add prevent="click submit mousedown" to your button, to prevent actual submission. Maybe only one of those 3 is really needed, but this combination gave me pretty consistent results.

OK, but the above only prevents form from actual saving data. It still gets validated, and that's not exactly what you want, right? I already got answer on how to omit form's validation at AJAX button click.

Other approach would be to either add custom form element, let's call it real_button, a copy of button but with type="button", or to change type on document ready using jQuery.

  • That was the trick - thanks for the help. I really appreciate it :) – Trevor Newhook May 20 '13 at 0:21
  • I guess it's a different problem now, but after I press the "add more" button, the ids for the text boxes change every time one is added or removed. Is there a way to keep them the same? I need to access the text boxes by id. – Trevor Newhook May 20 '13 at 2:58
  • I figured it out - in the for ($i = 0; $i < $form_state['num_names']; $i++) loop, I created an $elementID variable, then in the $form['names_fieldset']['name'][$i] array, I set the '#id' => $elementID, – Trevor Newhook May 20 '13 at 3:32
0

I struggled for 2 hours and I figured it out. In my case, in the place of modulename (e.g. godown) in the code snippet it actally had to be the form name (e.g. godown_item_form).

All along the way, I was replacing the modulename in the code with godown (my module's name), but when I replaced it with godown_item_form (my form's name) it started working like a charm.

  • Welcome to Drupal Answers! It's not clear to what snippet you are referring. Functions implemented from a module (especially hooks) need to be prefixed by the module name, not the form's name. While that worked for you, this is not the solution for everybody. It is rather probable there is another bug in your code, which is not the same the OP is noticing. – kiamlaluno Jan 28 '14 at 6:56
0

IMPORTANT note to everyone using this with a "Number of values" setting greater than one (read: If the user can add additional copies of the entire field type via the "Add another item" button.).

For over an hour now I have been struggling trying to fix a problem: After the AJAX callback triggered, an incorrect $delta value was used every time during the ajax form rebuilding process. To be more precise, it always used the $delta value referencing to the very last item in the list, regardless of whether I pressed the button of the first, second or last item.

The ridiculously simple solution to this involves to quick steps:

  1. Append the $delta value to the ajax wrapper.
    (This is optional really, and just to produce for correct markup, otherwise you would have multiple wrappers with the very same ID. Drupal's AJAX is still smart enough to target the correct wrapper regardless of the multiple ID copies.)

    $form['names_fieldset'] = array(
      '#type' => 'fieldset',
      '#title' => t('People coming to the picnic'),
      // Set up the wrapper so that AJAX will be able to replace the fieldset.
      '#prefix' => '<div id="names-fieldset-wrapper-'.$delta.'">',
      '#suffix' => '</div>',
    );
    
  2. Include the $delta value in the #name element of the "add one more" ajax button.
    this makes sure the system can distinguish between the "add one more" button of each item.

    $form['names_fieldset']['add_name'] = array(
      '#type' => 'submit',
      '#value' => t('Add one more'),
      '#name' => 'add-name-'.$delta, // <-- most important bit
      '#submit' => array('spower_add_more_add_one'),
      // See the examples in ajax_example.module for more details on the
      // properties of #ajax.
      '#ajax' => array(
        'callback' => 'spower_add_more_callback',
        'wrapper' => 'names-fieldset-wrapper-'.$delta, // <-- accompany the name wrapper IDs
      ),
    );
    

    Additionally, if you followed step 1 above, you will also need to update the wrapper attribute as seen in this code.
    And obviously the same will need to be done to the "remove one" button.

Again, the most important step is adding the unique #name attribute for all buttons for all items. Once I have added that, everything started working flawlessly here, as the correct $delta value is now being used after the callback function.

Hope this helps should anyone else ever run into this problem too.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.