Problem/Motivation

I have a slightly complicated form setup. The form has an multiple text boxes, an ajax-enabled select list, a table with multiple lines and multiple form elements, including a delete button, per line, one add button and, finally, a Submit button for the form.

So, in summary, the form has multiple ajax-enabled "delete line" buttons, one ajax-enabled "add line" button and one regular "Submit" button.
The delete line and add line buttons are working like a charm. But the problem is that the "Submit" button behaves erratically. If there are no validation errors, the form submits correctly. But, if I, for example, leave out a "required" field, I correctly get the form error message saying that that field is required. Now, if I submit the form again with this same error, the form just resets and reloads.
I checked the $form_state['clicked_button'] in my "_form()" callback and saw that the last "delete line" button was recorded as the pressed button.
To further confirm that this issue is popping up because of multiple "buttons" on the form, I changed the #type of the "delete" and "add line" elements to "radio" and this issue went away.

The following are the code snippets for the button elements inside my "_form()" callback:

//Set form cache to enable dynamic addition of bill line items to form via ajax calls.
    $form_state['cache'] = TRUE;
    
    if(isset ($form_state["clicked_button"]))
        watchdog('mymodule_add', var_export($form_state["clicked_button"],true));

/**This is from inside a loop **/
  $form['button_delete'] = array(
    '#type'    => 'button',
    '#value'   => t(" "),
    '#id'      => 'delete-line-'.$cleaned_key,
    '#name'    => 'delete-line-button-'.$cleaned_key,
    '#parents' => array('line', $key, 'button_delete'),
    '#ajax'    => array(
        'event'    => 'click',
        'callback' => 'purchase_item_delete',
        'wrapper'  => 'purchase-items',
        'method'   => 'replace',
        'effect'   => 'fade',
     ),
     '#submit' => array('purchase_item_delete'),
     '#executes_submit_callback' => FALSE,
     '#limit_validation_errors' => array(),
     '#attributes' => array(
                        'class'   => array('button_delete'),
                        'onclick' => 'jQuery("input[name=\'mymodule_ajax_cmd_array\']").val("remove_line|'.$key.'");',
                      ),
  );

/** Loop ends somewhere here**/
//

    //The button to add more line item rows.
    $lines['footer']['add_purchase_item'] = array(
      '#type' => 'button',
      '#id'   => 'save-and-add-line',
      '#name' => 'save-and-add-line',
      '#value' => t('Add Line'),
      '#title' => t('Add Line'),
      '#ajax' => array(
          'event'   => 'click',
          'callback'=> 'inventory_purchase_item_add_ajax',
          'wrapper' => 'purchase-items',
          'method'  => 'replace',
          'effect'  => 'fade',
      ),
      '#executes_submit_callback' => FALSE,
      '#limit_validation_errors' => array( array('line'), ),  /**Only validate the $form_state['values']['line'] elements.**/
      '#attributes' => array(
                        'onclick' => 'purchase_add_button()',
                      ),
    );

//Finally, the form's submit button.
$form['submit'] = array(
  '#type'   => 'submit',
  '#value'  => t('Save'),
  #'#id'     => 'form-submit-button',
  #'#name'   => 'form-submit-button',
  '#suffix' => ' | <span id="cancel_button"><a href="../inventory-purchases-list">Cancel</a></span>',
  '#weight' => 1000,
  '#attributes' => array(
      'onclick'=>"return purchase_save_button();",
  ),
);

Proposed resolution

As I mentioned, this issue went away once I changed my "delete" and "add" buttons to radio (or checkbox, and I suspect anything that supports #ajax will work, except for "submit", "button" and "image_button") (but I really don't want to!).

I also noticed that Drupal "forgets" to add any custom js library when there was a validation error in the form. I coped with this in my custom validation callback by re-attaching the js files after checking if there were any validation errors:

function mymodule_inventory_purchase_add_validate($form, &$form_state) {
    inventory_purchase_common_validations($form, $form_state); /*Here, I set form_error() for any incorrect user entry element*/
    if(form_get_errors()){
        $path = drupal_get_path('module', 'mymodule_inventories');
        drupal_add_js($path.'/res/mymodule_inventories.js');
    }
}

BUT, this only takes care of any custom validations. Basic validations like missing "required" field values are handled before my _validate() callback and hence do not re-attach the custom js files.
I only mentioned this because if Drupal determines callback buttons based on an external js (fapi's js?), then this could also be the reason for this bug.

I am also attaching a part of the form highlighting the buttons mentioned. Please let me know if any more code/clarification is required to solve this issue.

CommentFileSizeAuthor
form.png9.13 KBAnandkumar

Comments

andiart’s picture

Version: 7.12 » 7.14

I use Drupal core 7.14 and have the same behaviour of image buttons interfering the submit button click. The triggered and clicked elements are shown as the image buttons when clicking the submit button. Here they are:

$form_state['clicked_button'];
$form_state['triggering_element'];

Like Anandkumar seems to has tried I also renamed my buttons, which had not the wanted effect. (I named the submit button "op").

andiart’s picture

andiart’s picture

double post

David_Rothstein’s picture

Status: Active » Postponed (maintainer needs more info)

I tried to reproduce this with similar code as above, but didn't see any problems. I think this issue would probably need a complete (fully working and standalone) code sample to be able to test?

I also didn't see any problems with the JavaScript in the validation handler, although in general it's better to add JavaScript via $form['#attached']['js'] to avoid caching problems. I don't think that's necessarily the issue here though.

@andiart, your issue doesn't seem related since the original post wasn't specifically about image buttons. However, see #873070-27: When an image button appears after another button in a form, the wrong triggering element and #submit handlers are detected (and issues linked to within that) for more information. Making sure your image buttons use #name but not #value is one way to fix that in the meantime.

damien tournoud’s picture

Category: bug » support
Status: Postponed (maintainer needs more info) » Closed (cannot reproduce)
mbopp’s picture

To clarify this issue, and a solution.

Summary: (in my case) having more than one button in a table row with ajax properties and actions. Resulting in the occasional wrong callback being fired for a button, as well as other attribute mismatches.

Using the #name attribute is key. Make sure you have a unique #name attribute (which in a table, mean unique across the whole table/form, put a row identifier in the name). But in addition, you must also NOT use the #value attribute. If the #value attribute is set in addition to the #name attribute you run the risk of having unexpected results (ie. the wrong ajax callback firing, etc).

Hope this helps someone. It helps me to put it in writing somewhere.

ZalemCitizen’s picture

Thanks mbopp.

My form includes repeatable fieldsets (with add more button).
Then fields of each fieldset are built within a foreach loop.

I wasn't defining any #name for my ajax buttons and had same issues on Drupal 7.42.
The default #name 'op' was then used by drupal form system.
Though, défining #value and even same #value for each button is no problem.

marcelovani’s picture

Yep, had the same problem. adding #name fixed it.