1

I'm trying to make a form in Drupal, so more than one element can be added to a form. For example, a page might contain data for an event, then the event might have multiple dates. So I have a form that looks like:

/**
 * Implements hook_form_alter().
 */
function addextra_form_alter(&$form, &$form_state) {
  if ($form['#form_id'] == 'test_content_node_form' ) {
    $form['elements_table'] = array(
        '#theme' => 'table',
        '#title' => 'Elements already added',
        '#header' => array('Item', 'Remove'),
        '#empty' => 'No elements',
        '#prefix' => '<div id="elements-table">',
        '#suffix' => '</div>',
    );
    $form['add_elements'] = array(
      '#title' => 'Add another element',
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => FALSE,
    );
    $form['add_elements']['add_content'] = array(
        '#type' => 'textfield',
        '#description' => t('Add an element to the table'),
        '#title' => t('Add another item'),
        '#size' => '12',
        '#maxlength' => '60',
        '#prefix' => '<div id="addextra_content">',
        '#suffix' => '</div>',
    );
    $form['add_elements']['add_another_btn'] = array(
        '#type' => 'button',
        '#name' => 'add_another',
        '#button_type' => 'submit',
        '#executes_submit_callback' => FALSE,
        '#value' => 'Add another',
        '#ajax' => array(
            'callback' => 'addextra_element_to_table',
        ),
    );
  }
}

When 'add_another_btn' gets clicked, it will run the ajax callback 'addextra_element_to_table.

That callback is:

function addextra_element_to_table(&$form, &$form_state) {
  $form['elements_table']['#rows'][] = array($form_state['values']['add_content'], l('Remove Item', '#'));
  drupal_add_js(drupal_get_path('module', 'addextra') . '/addextra.js');
  return array(
      '#type' => 'ajax',
      '#commands' => array(
          ajax_command_replace('#elements-table', render($form['elements_table'])),

      ),
  );
}

The js file called replaces the val of the input field to ''

(function ($) {
  $('#edit-add-content').val('');
})(jQuery);

But this callback only gets called one time. I believe this is because the behaviour has to be attached again once it's been called. Sorry for my ignorance - I'm not sure how to achieve this. Can anyone help me out? It would be much appreciated. Thanks in advance.

3
  • I am not a Drupal expert but what you do is adding again and again the same js file in the already loaded html page and expect the $(document).onload() event to be called every time ? Looks weird. Why using Ajax to achieve that ? Can't you just clone a (eventually) hidden node ? Commented Dec 28, 2011 at 2:50
  • I can remove the js, but the form replace for the new element doesn't work either. It's not performing the operations in the callback where it adds the '#row' data. Commented Dec 28, 2011 at 3:00
  • I've moved the drupal_add_js and it's working as expected. Every time I add a new value to add_content, it does run and replace the table. But it's not adding to the '#rows', it's just replacing the rows with the new value rather than adding another element to the table then replacing the table. Any thought would be much appreciated. I know there are other ways to achieve this, but I now just want to figure out how to achieve it this way so I can use this method in the future. Commented Dec 28, 2011 at 8:49

1 Answer 1

4

The problem is that render(), which basically just calls drupal_render() , does not process the #ajax element, it's simply ignored. You might want to try and pass the element through ajax_pre_render_element() before the call to render().

That said, I personally don't have good experience with trying to reuse Drupal functions outside their normal calling sequence, especially not with forms. I prefer to stick to the very top level functions, such as drupal_get_form. I have followed those functions many times in my debugger, and they do a bunch of things in a precise order that is hard to get a hold of when you want to reuse pieces of that.

To alter a form with AJAX callbacks, I would always prefer one of two strategies:

  • In the callback, adjust the content of the $form argument and do return $form. This requires that you set #ajax['wrapper'] to the id (the value of the id attribute in the markup, as used for CSS) of the form on the originating element (the button in your case). Then Drupal gets to do its shpiel with the whole form, and the browser replaces the whole thing. Drupal takes care of preserving values already entered etc.
  • Alternatively, you can have the callback return a set of commands that do very specific modifications on the DOM. In your case that would be commands which create and append new rows. Keep in mind that with ajax_command_invoke(), you have the entire jQuery arsenal at your disposal.

From those two strategies, I usually prefer the second one, because it seems more elegant for little tweaks. However, if you want to build on Drupal's rendering, or if you have more massive changes to the form, you should use the first one.

As a side note, did you know that there is drupal.stackexchange.com? Drupal can be quite peculiar, and on that site, you'll find more experts on the specifics.

Sign up to request clarification or add additional context in comments.

1 Comment

I had the same issue and doing "return $form;" in the callback with the top level id fixed it instantly (vs. "return $form['some_element'];" to a targeted id). This is the only solution I was able to make so far. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.