0

I've found that dynamically created submit handles will adopt the first declared submit function in a form after an AJAX event has been processed. Demo code:

<?php

function form_test_main_form($form, &$form_state) {

  $form['form_wrapper'] = array(
    '#prefix' => '<div id="form_wrapper">',
    '#suffix' => '</div>',
  );
  $form['form_wrapper']['ajax_wrapper'] = array(
    '#prefix' => '<div id="ajax_wrapper">',
    '#suffix' => '</div>',
  );

  //trigger_message() function may be triggered without ever interacting with this button
  $form['form_wrapper']['invisible_button'] = array(
    '#prefix' => '<div style="display:none;">',
    '#suffix' => '</div>',
    '#type' => 'submit',
    '#value' => '',
    '#submit' => array('trigger_message'),
    '#id' => 'invisible_button',
    '#name' => 'invisible_button',
    '#ajax' => array(
      'callback' => 'rebuild_form',
      'wrapper' => 'form_wrapper',
    ),
  );

  $test_li = array('Option 1', 'Option 2');
  $form['form_wrapper']['choose_value'] = array(
    '#title' => 'choose button text',
    '#type' => 'select',
    '#options' => array_combine($test_li, $test_li),
    '#id' => 'choose_value',
    '#name' => 'choose_value',
    '#ajax' => array(
      'callback' => 'ajax_trigger',
      'wrapper' => 'ajax_wrapper',
    ),
  );
//Submit
  $form['form_wrapper']['submit_value'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
    '#submit' => array('submit_query'),
    '#id' => 'submit_value',
    '#name' => 'submit_value',
    '#ajax' => array(
      'callback' => 'rebuild_form',
      'wrapper' => 'form_wrapper',
    ),
  );

  $form['form_wrapper']['container1'] = array(
    '#prefix' => '<div id="container1"><br><br>',
    '#suffix' => '<br><br></div>',
  );
  if (isset($form_state['value_chosen'])) {
    $form['form_wrapper']['container1']['button_t1'] = array(
      '#type' => 'submit',
      '#value' => $form_state['values']['choose_value'],
      '#submit' => array('submit_query'),
      '#id' => 'button_t1',
      '#name' => 'button_t1',
      '#ajax' => array(
        'callback' => 'rebuild_form',
        'wrapper' => 'form_wrapper',
      ),
    );
    if ($form_state['values']['choose_value'] == 'Option 1') {
      $option_li = array(1 => '', 2 => 'style="display:none;"');
    }
    else {
      $option_li = array(1 => 'style="display:none;"', 2 => '');
    }
  }
  else {
    $option_li = array(1 => 'style="display:none;"', 2 => 'style="display:none;"');
  }

  //current solution is to create all desired dynamic buttons prior to fulfilment of conditional and hide until needed
  $form['form_wrapper']['container2'] = array(
    '#prefix' => '<div id="container2"'.$option_li[1].'>',
    '#suffix' => '</div>',
  );
  $form['form_wrapper']['container2']['button_t2'] = array(
    '#type' => 'submit',
    '#value' => 'Option 1',
    '#submit' => array('submit_query'),
    '#id' => 'button_t2',
    '#name' => 'button_t2',
    '#ajax' => array(
      'callback' => 'rebuild_form',
      'wrapper' => 'form_wrapper',
    ),
  );
  $form['form_wrapper']['container3'] = array(
    '#prefix' => '<div id="container3"'.$option_li[2].'>',
    '#suffix' => '</div>',
  );
  $form['form_wrapper']['container3']['button_t3'] = array(
    '#type' => 'submit',
    '#value' => 'Option 2',
    '#submit' => array('submit_query'),
    '#id' => 'button_t3',
    '#name' => 'button_t3',
    '#ajax' => array(
      'callback' => 'rebuild_form',
      'wrapper' => 'form_wrapper',
    ),
  );

  unset($form_state['value_chosen']);
  return $form;
}



function rebuild_form($form, $form_state) {
  return $form['form_wrapper'];
}

//ajax callbacks automatically associate with the first declared submit handle?
function trigger_message($form, &$form_state) {
  drupal_set_message('triggered!', 'warning');
  $form_state['rebuild'] = TRUE;
}

function ajax_trigger($form, $form_state) {
  $trigger_value = $form_state['values'][$form_state['triggering_element']['#id']];
  $form['form_wrapper']['ajax_wrapper'] = array(
    '#prefix' => '<div id="ajax_wrapper">Value of triggering element = '.$trigger_value,
    '#suffix' => '</div>',
  );
  return $form['form_wrapper']['ajax_wrapper'];
}

function submit_query($form, &$form_state) {
  drupal_set_message('submit_query()', 'warning');
  $form_state['value_chosen'] = TRUE;
  $form_state['rebuild'] = TRUE;
}

How to reproduce the observed behavior

  1. Submit the form
  2. Select a new option, triggering AJAX response
  3. Click top option button; note that trigger_message() is shown as warning despite button calling submit_query().
  4. Repeat steps 1 and 2 but click bottom option button. This was not dynamically created (instead dynamically concealed) and behaves as expected, triggering submit_query().

Dynamic concealment will likely produce a working form, but I find the idea of creating useless form elements before needing them to be a bad idea in general. Assuming other people are able to reproduce my problem, can you help me understand what I'm doing wrong or offer other ways to avoid this behavior?

  • Setting #name and #id causes weird issues in the system, and should be avoided. They seem like fixes, but end up not being fixes. You generally need to find another way around whatever issue you were trying to fix by setting those. It's better to let Drupal set them, as Drupal expects specific values for those to function. – Jaypan Sep 26 at 23:26
  • I have encountered problems when setting #name and #id before, but that was when I set them as attributes instead of properties. I usually set these values for form elements to simplify reading the contents of the form with $form_state['values'] and $_POST. – Ghoti Sep 26 at 23:42

Your Answer

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

Browse other questions tagged or ask your own question.