FAPI button handling uses #value as #op when the form is submitted. This prevents developers from using the same label on two buttons. Consider a form similar to a node form, where more than one field contains an "Add Another" option:

  // Some input fields ...

  $form['button1'] = array(						
    '#type' => 'button',
    '#value' => t('Add Another'),
  );

  // Some more input fields ...

  $form['button2'] = array(						
    '#type' => 'button',
    '#value' => t('Add Another'),
  );

In the submit handler, $form_state['triggering_element'] will always point to the first button, even if the second one was clicked. Node forms do not use FAPI; they have a different mechanism for generating forms that includes the machine name of the element and thus are not affected by this bug.

Another situation is where 2 form buttons ('Cancel' and 'Continue') have different values but one button 'Continue' was initially disabled on render and enabled via javascript after an ajax login was completed. Clicking on that 2nd button 'Continue' now was always triggering the 'Cancel'. So there are two and only two submit buttons - both have name="op", but their values are different. If the first button is initially disabled in a HOOK_form_alter, it appears the triggering element will get set to the value of the 2nd button.

CommentFileSizeAuthor
#17 fapibutton.zip1.3 KBblainelang
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Anonymous’s picture

Version: 7.9 » 7.x-dev

can you write a test that illustrates this?

or a simple module? it's a bit hard to visualise the problem from your description.

webchick’s picture

Category: bug » support
Status: Active » Postponed (maintainer needs more info)
tgf’s picture

The visualization is a grouped (or tabular) layout for each record with a select and delete button per group. The goal is to relate each of the buttons to items in the group/row. The buttons are named using the item/row number so they can be identified on click and then an action taken for the specific item.

If you use the following form definition it will generate 10 rows of data:

  debug($form_state['triggering_element']);
  for ($item = 0; $item < 10; $item++) {
    $form['button_group'][] = array(
      '#tree' => true,
      'item' => array(
      	'#markup' => '<br />Item ' . $item . ' ',    
      ),
      'select' => array(
        '#type' => 'button',
        '#value' => t('Select'),
        '#name' => 'select_department_' . $item,
        // The following syntax always returns the first button on the page
        //'#name' => 'select_department[' . $item . ']',
      ),
      'delete' => array(
        '#type' => 'button',
        '#value' => t('Delete'),
        '#name' => 'delete_department_' . $item,
        // The following syntax always returns the first button on the page
        //'#name' => 'delete_department[' . $item . ']',
      ),
      'break' => array(
        '#markup' => '<hr />',
      ),
    );
  }

The triggering_element worked fine for me each time to identify the buttons with their name and item number.

What didn't work for me was trying to use array syntax for the button names such as "delete_department[0]". This always returned the first button on the page as the triggering_element.

Hope that helps to clarify the situation. I can't reproduce the bug other than when using the array syntax.

crobinson’s picture

It looks like the Javascript POSTs back the label of the button as 'op'. And 'op' is then used to uniquely identify which button was pressed.

I just confirmed this with a simple form with two buttons. If the buttons both have the same #value, then $form_state['triggering_element'] is identified as the second button regardless of which button was clicked. If the buttons have unique #values, the buttons work as they should.

This doesn't seem like a bug so much as a design flaw/characteristic. Buttons should ideally have an operation code that's separate from their label. Note that this only applies to FAPI forms. Node forms use different Javascript, so even if they have multiple Add More buttons, they don't have this problem because instead of passing in op, they pass in triggering_element_name and triggering_element
_value. Could that behavior be re-used for FAPI forms?

interX’s picture

Category: support » bug
Priority: Major » Normal
Status: Postponed (maintainer needs more info) » Active

I confirm that it only happens when the button labels are identical, as said in #4. It is easily reproducable.

Actually, to me this does feel like a bug, as there are many valid cases where you can have several buttons on a form with an identical label. A unique label per action can not be a design characteristic.

As a quick & dirty bypass, you could add some temporary code and replace it in the theme layer or add your own theme function for those buttons with a fixed label.

crobinson’s picture

That works. You can also make sure your buttons always have different names. That seems like the wrong "fix" because of the tie between the label and the machine name. But it does work if you don't mind snuggling up to your thesaurus.

czigor’s picture

I agree with interX that this is a bug.

As a workaround I did what was suggested in #5 with a small change. I temporarily gave different #values for each button. Then I did not use a #theme function but a #pre_render function to rewrite their #values to the same.

crobinson’s picture

Given that changing this behavior might be a pain for D7, should it be pushed to D8 instead but with an elevated priority? It's definitely a bug but there are multiple workarounds and it really only affects back-end developers who have the capacity to use those workarounds...

bleen’s picture

Version: 7.x-dev » 8.x-dev
Issue tags: +Needs backport to D7

re #8: all bugs should be fixed in D8 before being backported to D7 ... as for elevating, I dont think this is particularly "major" based on http://drupal.org/node/45111

blainelang’s picture

I think this situation also is what I'm seeing on the commerce checkout form where I have disabled the 'Continue to next step' submit button until you login (inline ajax login form) and then it's re-enabled via JS. After the 'Continue' button is enabled via JS, and then used it triggers the 'Cancel' logic and not the 'Continue' logic. So there are two and only two submit buttons where name="op" in both cases, but their values are different. If the first button is initially disabled in a HOOK_form_alter, it appears the triggering element will get set to the value of the 2nd button.

crobinson’s picture

Priority: Normal » Major
Issue tags: +DrupalWTF

Adding tag DrupalWTF, as this is unexpected/undesirable behavior. Also, re-elevating to Major. I think the priority is worth discussion here. The document bleen18 quotes says this about Major:

"Major priority is also used for tasks or features which consensus has agreed are important (such as improving performance or code refactoring), but which are not functional bugs."

I would argue that there is consensus here that this is important (certainly nobody has said that it is not), it will definitely help code refactoring, and it's a structural problem in Drupal FAPI. It should at least be strongly considered for D8, especially since there are open discussions about other things.

There are a number of related tickets (they're not duplicates, but supporting material / ideas) open as well for D8:
http://drupal.org/node/1719640
http://drupal.org/node/852520
http://drupal.org/node/1671190

tim.plunkett’s picture

Priority: Major » Normal
Issue tags: +Needs issue summary update

This doesn't sound like a major bug to me. Also, it could use a better issue summary.

crobinson’s picture

The priority system seems very subjective to me - the definition in the document for "Major" seems to very clearly fit this bug. Priorities on tickets are regularly changed based only on phrases like "sounds like", leaving the rest of us wondering what we're doing wrong. If this isn't Major, perhaps somebody should update the definition to clarify to bug-reporters what, exactly, is considered Major. I'm sure as heck confused.

I don't want to get into a Wikipedia-style edit war so I'm not going to re-elevate this, but it seems ironic to me that we've all spent more time discussing the bug than it would probably take to fix it. I'm working my way up to contributing to core; maybe I'll give it a go as my first target if nobody beats me to it.

I updated the issue summary to home in on the precise problem. Hopefully it's clearer now.

crobinson’s picture

Issue summary: View changes

tim.plunkett has requested a "better issue summary".

blainelang’s picture

@crobinson, I agree with your observation on this issue. It's surprising it's not been more visible as problem. I added a 2nd example where this issue occurs and was not able to find a work around.

Damien Tournoud’s picture

Category: bug » support
Status: Active » Closed (won't fix)

This is not a bug. The unique key for a button is #name, #value.

blainelang’s picture

Damien, understand and I will work on test module that will demonstrate the issue (or non-issue) and upload it for review.

blainelang’s picture

FileSize
1.3 KB

Attached is a test module that demonstrates the problem @crobinson noted in the updated summary but as Damien noted, this may very well be the expected behaviour currently.

The module also demonstrates issue 2 that I reported, where if you use 2 different values for the buttons but disable one initially (in the form definition) but enable it in javascript (as in after certain fields have been validated), then the 2nd button will trigger the first button's submit handler.

blainelang’s picture

Status: Closed (won't fix) » Active
wojtha’s picture

I'm now working on the #1452894: Elements with #has_garbage_value and #value are always set as a triggering element and I've found that every element with non-empty #has_garbage_value and non-empty #value is always set as triggering element and if there are multiple elements of that kind in the form, the last one is picked up. (See the second condition in _form_button_was_clicked() function).

Maybe that information will be useful here too.

Jon Nunan’s picture

The opening example and the example module in #17 are both leaving out the '#name' attribute, so the button is defaulting it's name to 'op'. If you simply provide the button with a unique '#name' value like #3 pointed out, you'd be fine.

With most examples in the documentation leaving out '#name' I can see how people can fall into this trap. Maybe the default '#name' should be generated so its unique instead of being a generic 'op' ?

ro-no-lo’s picture

As a note. Damien's note in #15 is more descriptive as the offical documentation here: http://api.drupal.org/api/drupal/developer!topics!forms_api_reference.ht... which just is "String". I never heard of that usage either and I'm a Drupal dev since 5.x.

Edit: It seems that api.drupal.org web addresses are to hard to parse so they turn not automaticly into links, even though the current filter says so.

selvamkf’s picture

Awesome, thanks @meastack.

blainelang’s picture

#20 - @meatsack, even leaving out #name and it defaulting to 'op' for the field name, the #value is still unique so according to the API and notes in this issue, it should work. I wanted to use the same name so that I could test for the selected form operation.

zhangtaihao’s picture

So the confusion surrounds the assumption that a submit button somehow automatically becomes uniquely identifiable by virtue of the Form API. It doesn't: that's the first point to be clarified in the docs.

Second, people encountering this issue either encountered the Drupal Form API before plain HTML forms (i.e. did not learn about what form submissions are in HTML/HTTP terms) and carried on with erroneous expectations or are creating very complex form trees and/or custom nested elements with submit buttons. The plain fact of HTML is that the name-value combination of a submit button is as unique as a form submission gets without JS intervention.

As I see, there are two possible resolutions (among perhaps other more ingenious ones): refactor the Form API to uniquely process submit buttons with the same #value's (or even additionally with the same custom #name's), or construct a reference implementation for complex forms with overlapping name-value buttons. Given the former is not likely to happen (as this is more of a misunderstood/niche case), the example implementation would make more sense, possibly a candidate for request to merge into Examples for Developers. This is a documentation/communication issue, not a bug, and should be resolved as such.

Aside: I have been able to do multiple same-value submit buttons (embedded inside composite element types) by constructing the name out of a concatenation of #parents, in the style of field forms buttons (think "Remove" and "Add another item"). Sure, it's not absolutely reliable, but if your basic button name is unique enough it usually doesn't matter. Perhaps this could be highlighted in the docs or in an example implementation.

zhangtaihao’s picture

Alternatively, submit buttons need to be refactored with deep-tree support. A brief glance at the form submission process reveals that the following will have to be refactored:

  • _form_button_was_clicked() to recognize a button as "deep" and drupal_array_get_nested_value() instead.
  • _form_element_triggered_scripted_submission() to do the same as well as understand deep names (i.e. with ][ in the middle).
  • ajax_pre_render_element() to encode the correct _triggering_element_name for client-side use.

There may be additional server-/client-side issues introduced by this change, so don't anybody hold his or her breath.

zhangtaihao’s picture

Issue summary: View changes

Added a 2nd situation -where the 2nd form button is initially disabled.

leex’s picture

I have a situation where #value is the same but #name is different. Everything works but if #name has a space in it name="some name" then triggering_element becomes the first rendered element with the same #value.

sun’s picture

Issue summary: View changes

I've the impression that the root cause for this issue is being addressed in:

#1279688: Do not use translated button caption to determine clicked buttons.

Shall we mark this issue as a duplicate, or do you think there further fixes required for the triggering_element detection?

kolier’s picture

Each button need to have an unique #name if there are multiple buttons in the form, then triggering_element will do the job.

joseph11’s picture

Ad #29: thanks, @kolier, it works.

lhangea’s picture

I can confirm that if you have multiple buttons and you don't set a unique name for each of them then the triggering element is not correctly identified.

IRuslan’s picture

Just for whom is interested i provide small backporting patch for D7 to make it possible to handle by #name multiple submit buttons — #2546700: Add support for detecting the triggering element when buttons are changed client-side

sanduhrs’s picture

Category: Support request » Bug report
$form['item_1'] = array(
    '#type' => 'submit',
    '#value' => t('Submit me 1'),
    '#name' => 'name_1',
);
$form['item_2'] = array(
    '#type' => 'submit',
    '#value' => t('Submit me 2'),
    '#name' => 'name_2',
);

Other than stated in #29 twelve months ago:
Even if i define unique names for the submit elements, in current D8 the triggering element is always 'name_1' no matter what button was pressed.

yannickoo’s picture

I have this when I'm trying to render a form inside a form. Imagine you have a big form and for selecting some value there is a provided view with exposed filters and an ajaxified submit button. I'm using Field Collection in another section on that outer form and when I'm trying to click the Remove button in order to delete a Field Collection item, Drupal thinks I have clicked the Apply button which lives inside the view exposed filter form.

This is so annoying :(

David_Rothstein’s picture

Title: FAPI identifies wrong button as 'triggering element' » Document that buttons with the same #value need a unique #name for the form API to distinguish them, or change the form API to assign unique #names automatically
Category: Bug report » Task

I'm retitling and recategorizing this based on @zhangtaihao's comments in #24 and onward.

I also tested the code example in #33 in both Drupal 7 and 8 and couldn't reproduce any problem (and additionally tested the equivalent code example where #value was the same between the two buttons - that worked fine for me as well). If there is something broken here, it needs more detailed steps to reproduce.

David_Rothstein’s picture

sanduhrs’s picture

The behavior described in #33 is gone.
Just checked with the latest 8.0.x where two buttons with unique #name are working just fine.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Warl’s picture

With currently installed v1.3 version I had the same behavior with a Form class, which has validate and submit handlers and is called multiple times from a Field Formatter to create "Book session" buttons in a list with additional content.

Nothing of previously described worked for me.
Not unique #value as in #4.
Not unique #name as in #20, what shows that #29 is definitely not the case, may be not always.

What worked for me was to give unique name with id number, as well as generating unique FormID, what was essential.

public function getFormId() {
  static $uniqueid = 0;
  return 'session_booking_form_' .  $uniqueid++;
}

Version: 8.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Pascal-’s picture

Please fix this already,
I just spend a whole day trying to figure out why my form was returning a wrong TriggeringElement ...

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

esolitos’s picture

Another day, another 6y old bug encountered. :)
If I get some time from my boss after Easter I'll try to narrow this down...

Version: 8.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

kevinquillen’s picture

I think this bug persists in 8.4.4.

I have a form that I am showing on the same page twice, with different arguments. One is in the footer, and one is in a block. The block one just has a tagline before the email input.

The form is generating a unique ID like others have noted here, but I had to take an additional step. Since the form is the same definition but has a different ID, nothing in this thread worked other than giving my submit element a unique name, like this:

    $form['submit_' . $this->getFormId()] = [
      '#name' => 'submit_' . $this->getFormId(),
      '#type' => 'button',
      '#value' => $this->t('Sign up'),
      '#limit_validation_errors' => TRUE,
      '#submit' => ['::submitForm'],
      '#suffix' => '</div>',
      '#ajax' => [
        'wrapper' => $wrapper,
        'method' => 'replace',
        'callback' => '::validateEmailSubmission',
        'progress' => [
          '#type' => 'none',
        ],
      ],
    ];

Simply using '#name' had no effect. I also noticed that '#name' was not reflected in the data attribute on the form tag.

Its worth noting that the second form (and presumably any other forms rendered after the first) worked if I took the first one off the page. With two, it didn't even do the ajax callback, it just submitted like a normal form.

Only when I did $form['submit_' . $this->getFormId()] did both forms begin working as expected. I wasn't sure of any other way to resolve this.

I am not sure if this is potentially related or not: #2821852: The same form twice on one page with different arguments may process the wrong form when submitted

Version: 8.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

joachim’s picture

> or change the form API to assign unique #names automatically

That's the subject of #852520: Replace reliance on 'op' with 'triggering_element' to allow for identical submit button labels without having to manually specify a unique #name.

How about we make this one just a documentation issue? https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21... could really do with details on this.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.2.x-dev

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.2.x-dev » 9.3.x-dev

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

solideogloria’s picture

Version: 9.5.x-dev » 11.x-dev

Knowing this helped me fix a bug. It'd be incredibly helpful to document this.

paulmckibben’s picture

(deleted comment - sorry)

solideogloria’s picture

The issue summary was updated/improved in #13 but the tag wasn't removed after.

solideogloria’s picture

Component: forms system » documentation