1

am implementing a form and in that form it requires that two form element should be coming under one Add more button, so user can add any number of fields.

Here is the code which i am using to create a form.

function ajax_example_add_more($form, &$form_state, $no_js_use = FALSE) {
  $form['description'] = array(
    '#markup' => '<div>' . t('This example shows an add-more and a remove-last button. The <a href="!ajax">AJAX version</a> does it without page reloads; the <a href="!multistep">non-js version</a> is the same code but simulates a non-javascript environment, showing it with page reloads.',
      array('!ajax' => url('examples/ajax_example/add_more'), '!multistep' => url('examples/ajax_example/add_more_no_js')))
    . '</div>',
  );

  $form['#tree'] = TRUE;
  $form['names_fieldset'] = array(
    '#type' => 'fieldset',
    '#title' => t('People coming to the picnic'),
    '#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'][$i]['name'] = array(
      '#type' => 'textfield',
      '#title' => t('Name'),
    );
    $form['names_fieldset'][$i]['surname'] = array(
      '#type' => 'textfield',
      '#title' => t('SurName'),
    );
    $form['names_fieldset'][$i]['filedepot_file'] = array(
      '#type' => 'managed_file',
      '#title' => t('Choose a file'),
      '#size' => 22,
      '#process' => array('_module_my_file_element_process'),
    );
  }
  $form['names_fieldset']['add_name'] = array(
    '#type' => 'submit',
    '#value' => t('Add one more'),
    '#submit' => array('ajax_example_add_more_add_one'),
    '#ajax' => array(
      'callback' => 'ajax_example_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('ajax_example_add_more_remove_one'),
      '#ajax' => array(
        'callback' => 'ajax_example_add_more_callback',
        'wrapper' => 'names-fieldset-wrapper',
      ),
    );
  }
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Submit'),
  );
  if ($no_js_use) {
    // Remove the #ajax from the above, so ajax.js won't be loaded.
    if (!empty($form['names_fieldset']['remove_name']['#ajax'])) {
      unset($form['names_fieldset']['remove_name']['#ajax']);
    }
    unset($form['names_fieldset']['add_name']['#ajax']);
  }

  return $form;
}

/**
 * Callback for both ajax-enabled buttons.
 *
 * Selects and returns the fieldset with the names in it.
 */
function ajax_example_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 ajax_example_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 ajax_example_add_more_remove_one($form, &$form_state) {
  if ($form_state['num_names'] > 1) {
    $form_state['num_names']--;
  }
  $form_state['rebuild'] = TRUE;
}

function _module_my_file_element_process($element, &$form_state, $form) {
    $element = file_managed_file_process($element, $form_state, $form);
    $element['upload_button']['#access'] = FALSE;
    return $element;
}

Now after adding file when i click on +Add More button at that time all the fields retains their values but filefield gets reset.

Here i am hiding the upload button so that file will be uploaded when user submits the form. After filing on fieldset when i click on +Add more button at that time selected files doesn't come in the appropriate form fields, where as other fields retains their values.

How can i fix the code so that selected file remains as it is in the respected fields.

1

Problem is in $form_state['rebuild'] = TRUE; in your ajax_example_add_more_callback(). From my experience it sometimes is needed, and sometimes mess up values. So you could try to avoid it (point 1) or save values in a safe place before you rebuild, restore them after you rebuild (point 2).

  1. Way I prefer to do it, if feasible, is to prevent submit on a button that's supposed. I used $form_state['triggering_element'] in form building function to detect AJAX calls and decide what needs to be updated.

    function ajax_example_add_more($form, &$form_state, $no_js_use = FALSE) {
      if ( $form_state['triggering_element'] == MY_AJAX_ADD_BUTTON ) {
        $form_state['num_names']++;
      }
    

    That way you are not rebuilding form, so data has no opportunity to get discarded.

  2. The way that would require less changes to you is to save already filled values in some kind of cache inside your add more submit handler, and then set them as defaults in form building function.

    function ajax_example_add_more($form, &$form_state, $no_js_use = FALSE) {
      // (...)
      $form['names_fieldset'][$i]['name'] = array(
        '#type' => 'textfield',
        '#title' => t('Name'),
        '#default' => _mymodule_read_from_cache($i, 'name'),
      );
      // (...)
    }
    
    function ajax_example_add_more_add_one($form, &$form_state) {
      $form_state['num_names']++;
      $form_state['rebuild'] = TRUE;
      _mymodule_save_to_cache($form_state['values']);
    }
    

    This would require you to write _mymodule_save_to_cache() and _mymodule_read_from_cache() functions, but that's a way to prevent value loss during form rebuild. Not nice, maybe, but works.

  • Can you please describe little more, as i am not able to get your answer.. – mohit_rocks Jul 2 '13 at 8:43
  • @mohit_rocks Added pseudocode samples – Mołot Jul 2 '13 at 8:53
  • without writing $form_state['rebuild'] = TRUE; i am not able to get several instances of form. i.e. i can't get the output after pressing add more button. – mohit_rocks Jul 2 '13 at 11:14
  • @mohit_rocks from my experience $form_state['rebuild'] = TRUE; sometimes is needed, and sometimes mess up values. So my answer, point 2, can be read as: "save values in a safe place before you rebuild. Restore them after you rebuild". Makes sense? – Mołot Jul 2 '13 at 11:16
  • How can i save the files as these are not yet uploaded . Actually problem is with files only rest of things are working perfectly – mohit_rocks Jul 2 '13 at 11:21

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.