2

I am trying to implement a "add x" on a select field but it doesn't seem to work.

I've tried to follow this example but they use a submit for that.

$form['myfield'] = array(
    '#type' => 'select',
    '#options' => $optionsServer,
    '#ajax' => array(
        'callback' => 'ajax_select_change',
        'wrapper' => 'names-fieldset-wrapper',
        'method' => 'replace',
        'effect' => 'fade',
    ),
);

$form['#tree'] = TRUE;
if (empty($form_state['num_names'])) {
   $form_state['num_names'] = 1;
}
$form['myfieldset'] = array(
        '#type' => 'fieldset',
        '#title' => t('myfieldset'),
        '#prefix' => '<div id="names-fieldset-wrapper">',
        '#suffix' => '</div>',
    );
for ($i = 0; $i < $form_state['num_names']; $i++) {
   $form['myfieldset']['field'.$i] = array(
       '#type' => 'textfield',
       '#title' => t('field'),
       '#size' => 60,
       '#required' => TRUE,
   );
}

function ajax_select_change($form, &$form_state){
    //$form_state['num_names'] is used to create x new textfield
    $form_state['num_names'] = $form_state['values']['myfield'];
    //rebuild this form to get the new count
    $form_state['rebuild'] = TRUE;
    //change the fieldset containing the code to build textfield
    return $form['myfieldset'];
}

Is it doable? I can try to create a button then trigger it when I change the select value but this is clearly a crappy hack.

Any idea?

EDIT: I find out some solution:

function my_form($form, $form_state){
    $form['container']['myField1'] = array(
        '#type' => 'select',
        '#options' => $optionsField1,
        //don't forget the $form_state['values']['container']['myField1'] as default, it will keep the same value after the rebuild
        '#default_value' => isset($form_state['values']['container']['myField1']) ? $form_state['values']['container']['myField1'] : $form_state['num_names'] ,
        '#ajax' => array(
            'callback' => 'ajax_select_change',
        ),
    );
}


function ajax_select_change($form, $form_state){
    $new_state = array();
    $new_state['build_info'] = $form_state['build_info'];
    $new_state['rebuild'] = TRUE;
    $new_state['values'] = $form_state['values'];
    $new_state += form_state_defaults();

    $new_form_array = drupal_rebuild_form('my_form', $new_state);
    $new_form = drupal_render($new_form_array);

    $commands = array();
    //reload form
    $commands[] = ajax_command_html('#formId', $new_form);
    //change inside Id #price
    $commands[] = ajax_command_replace('#price', drupal_render($form['container']['myField2']));
    return array('#type' => 'ajax', '#commands' => $commands);
}

EDIT 2: see the proper solution below (in the comment), much more simple and make more sense!

  • Although your code may work is not the right way. See my answer for the Drupal way. Ah, and it's ok to answer your own question if you have found a solution for your problem. You can even get karma for upvotes on your answer yo your own question ;-) – sanzante Mar 30 '15 at 16:49
  • I chose your solution which was much more simple :D Thank you! – GwenM Mar 31 '15 at 11:44
2

After an AJAX submit by a select or some similar not submit element the Form API rebuilds de form if no validate errors are found. See drupal_process_form() where this is stated in the inline comments:

// However, for forms that have not been fully executed (e.g., Ajax
// submissions triggered by non-buttons), there is no submit handler to set
// $form_state['rebuild']. It would not make sense to redisplay the
// identical form without an error for the user to correct, so we also
// rebuild error-free non-executed forms, regardless of
// $form_state['rebuild'].

Your code is not working because you update your number of elements in the callback function in this line:

$form_state['num_names'] = $form_state['values']['myfield'];

The callback function is executed after all form processing so your modifications are done too late. In fact, callback function should only return the response, no modifications on the form should be done here.

Try something like this:

$form['myfield'] = array(
    '#type' => 'select',
    '#options' => $optionsServer,
    '#ajax' => array(
        'callback' => 'ajax_select_change',
        'wrapper' => 'names-fieldset-wrapper',
        'method' => 'replace',
        'effect' => 'fade',
    ),
);

$form['#tree'] = TRUE;
if (empty($form_state['num_names'])) {
   $form_state['num_names'] = 1;
}
else {
  // If there's is value in myfield update number of available names elems.
  $form_state['num_names'] = $form_state['values']['myfield'];
}


$form['myfieldset'] = array(
        '#type' => 'fieldset',
        '#title' => t('myfieldset'),
        '#prefix' => '<div id="names-fieldset-wrapper">',
        '#suffix' => '</div>',
    );
for ($i = 0; $i < $form_state['num_names']; $i++) {
   $form['myfieldset']['field'.$i] = array(
       '#type' => 'textfield',
       '#title' => t('field'),
       '#size' => 60,
       '#required' => TRUE,
   );
}

function ajax_select_change($form, &$form_state){
    return $form['myfieldset'];
}
  • this is the right solution I was looking for! so simple compared to the one I came out with... I don't even get why I didn't think about it! Cheers – GwenM Mar 31 '15 at 11:49

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.