Problem/Motivation
As per this very old forum discussion, creating form element buttons with a type of anything other than type="submit" is not possible - type="submit" is hard-coded.
Steps to reproduce
Proposed resolution
Add an additional #submit_button key to #type button, defaulting to TRUE. When this is FALSE, remove the type=submit related logic from the output.
Remaining tasks
Create a failing functional test that the MR makes pass. If possible utilise one of the existing test forms. Also try to add the test to an existing test class.
Since this change affects the submit #type indirectly, update docblocks and comments on submit and button #types accordingly.
Create a new child issue: demonstrate the new #submit_button key in the examples project's form API module.
User interface changes
N/A
Introduced terminology
N/A
API changes
A new key '#submit_button' is added to the button form #type. The default value is TRUE. That preserves the current behaviour of the button #type for backward-compatibility reasons. However, if changed to FALSE, the button element will no longer submit when clicked. Clicking will not trigger a page refresh.
Data model changes
N/A
Release notes snippet
The change allows the developer to toggle off form submission functionality with one line of code and no need to resort to multi-line workarounds.
| Comment | File | Size | Author |
|---|
Issue fork drupal-289240
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 289240-allow-type-button
changes, plain diff MR !9525
Comments
Comment #1
Ryan Palmer commentedShorter title... hopefully more eyeballs.
Comment #2
dman commentedIs this the problem you are addressing?
http://drupal.org/node/72855
There is a discrepancy between what Drupal API thinks is a form '#type'=>"button" element and what HTML thinks is a form type="button" element. :(
If you create a
'#type'=>"button"you actually get<input type="submit">which sends the form back to the server but the Drupal FAPI doesn't actually run the submit code :-}You do not get
<input type="submit">in any way. This is a huge terminology disconnect.There still is, AFAIK, no supported way to add a proper HTML 'button' or 'reset' element other than by having it into #markup
The PROBLEM however is that it's now basically impossible to switch back to making Drupal-button == HTML-button as all throughout the codebase 'buttons' are being used to submit (and preview and validate). See the discussion above. Unfortunately this means your patch will KILL a large part of current Drupal functionality!
The API ref could probably have better documentation explaining that even 'buttons' are submitted to the server.
And we could add a new type of 'button' to actually produce HTML buttons to disambiguate. ... Although it may have the opposit e effect.
I've learnt to live with the idea that actionless buttons are not a Drupal server-side job, and that 'reset' buttons are entirely useless. So since then I've not needed other button types. Except for AJAX, and this just forces me to keep my Javascript "unobtrusive"... although it's still awkward at times.
Ya see what I'm saying ? :-{
Comment #3
Ryan Palmer commentedI see what you're saying, and my bad for not digging a bit deeper before posting this issue.
So should this issue be refocused as "Change all those damn submit buttons in Drupal to the way they were meant to be written"? :)
Marked as duplicate.. will pick up with the original issue.
Comment #4
dman commentedThe change ain't going to happen, as there IS a significant use for the send-data-to-server-but-don't-run-FAPI-submit drupal button. It's very widespread. And useful as it is.
HTML type=button buttons basically do absolutely nothing without javascript. And are used very rarely.
A clarification that (button != button) and work-around in the documentation is the most likely way forward.
.dan.
Comment #5
David Stosik commentedAnd what about the reset button, which may be needed in some forms, but still impossible to create other than using #markup?
Thanks,
David
Comment #6
dman commentedThe reset button is an un-feature ... especially in the days of AJAX and multi-step forms.
Comment #7
ro-no-lo commentedI am not quite with you. I for myself would like to have an button, where I can attach javascript events which does absolutly nothing, when JS is not enabled. I.E. does not submits the form.
This might be an usability issue, but this can be solved via text or so.
To have an simple example. I would like to populate one form from anthoer as a little helper. (textarea lines into multiple textfields). This costs me 20 lines of JS or so and is much harder on the server side because the textfields are CCK managed and I don't want to manually hack there something. At least at first.
Comment #8
visum commentedLooks like a mostly dead discussion at this point, but here are my two cents:
I agree with those who think FAPI should let us make a element.
There are instances in which we like to use FAPI to describe and render forms, but do not need the fancy submit handling and such. For example, I've got a form I'm working on now that simply use jQuery to read the field values and generate Google Maps directions. No submit needed or wanted. I suppose the markup type can be used, but FAPI seems somewhat incomplete without the ability to generate all of the useful form elements.
Comment #9
damien_vancouver commentedI found a great workaround for this in Drupal 7, thanks to This StackOverflow question. To quote from the answer there:
You can use:
To disable the "submit" step.
If you only want to disable the "validate" step, use:
This appears to be contrary to what the handbook page Howto: Taming the Form Buttons. has to say. I suspect that the behavior may have changed for Drupal 7?
Anyway, it worked great for me when I added both in there. My use case is I added a button in hook_form_alter, which is supposed to fire off an #ajax callback. So obviously I don't want it validating nor do I want it submitting the form.
Comment #10
whatsmyline commentedWow, this issue is absurd. I want this fixed so badly. I TOTALLY agree with the OP.
Comment #11
coredumperror commentedI also agree with whatsmyline, and the OP: it is unacceptible that there's no way to create an
<input type='button'>element without using #markup, since doing so rules out using the majority of the awesomeness from the Forms API.However, since the existing
#type => 'button'does have a legitimate use, maybe you could implement a new one, like'#type' => plain_button?This inability to create non-submit buttons is a major UX issue for module writers who want to make forms with more than one button. Since every button is automatically a 'submit', pressing Enter while a form field is focused causes the first button in the form to be executed, rather than the actual form submission button (e.g. the OK button). By allowing us to make
<input type='button'>elements, we can control which button the browser submits when the user presses Enter.Comment #12
andypostSuppose related #1719640: Use 'button' element instead of empty links
This require now to just write a new
theme_button()function, change a process callback and fix render testsComment #13
jbrown commentedThis really is a pain in the ass.
type="submit" input elements cause all sorts of browser activity such as trying to remember passwords elsewhere in the form, etc.
You can use '#type' => 'markup', but then you loose all the AJAX functionality.
Maybe we should be able to specify '#element_type' ?
Comment #18
vomitHatSteve commentedSeems to me the solution here is to make it so that bad defaults don't forcibly override user-supplied values.
Attached is a patch that makes buttons respect the "type" attribute if provided.
Ostensibly, this shouldn't break any existing functionality that depends on the default behavior.
Comment #19
andypostStart CI on patch
Comment #21
darvanenI'd call #18 a workaround rather than a solution, but considering how damn long this problem has existed - I think it's definitely worth doing, at least in the short term.
The form API has both 'submit' and 'button' types, I really don't think buttons should automatically submit the form.
What is stopping us from setting the default of a 'button' element to type="button"? Or even just making '#executes_submit_callback' = FALSE work somehow?
Comment #31
seanbAnother thing to consider is that the first element with input type submit is triggered when pressing the enter key in any of the form fields. See the list of issues related to this. Using
<input type="button">for AJAX buttons would solve this.#2483505: Not possible to submit forms with pressing ENTER, if form has a file field
#212423: Standardize form submission behavior when "enter" key is pressed
#927176: "Enter" key removes file instead of saving node
#2030659: Disable 'enter' submission on extend page
#472698: admin/settings/performance: Keyboard <enter> key clears cache instead of applying settings
#2103017: Pressing the enter key in a variety of fields triggers form submit
#2785283: Enter key press in autocomplete results lead to ajax form submission
#1264794: Views exposed autocomplete + ajax view = form submit happens too early
#2897855: Entity browser modal dialog opens by pressing enter in any text field
Not sure how we can do this in a backward compatible way? I guess we need to introduce a new render element for it, and officially promote that so modules implementing AJAX buttons can start using it.
How about adding a new
#type = 'ajax_button'element for this purpose?Comment #32
darvanen#31 sounds sensible to me.
Comment #33
smustgrave commentedHiding #18
#31 does sound like a good approach. Tagging for framework managers for their thoughts.
Once that's decided the issue summary would need to be updated.
Comment #34
larowlan#9 seems to be the way forward here, is this just a docs issue?
Comment #35
larowlanComment #37
darvanen@larowlan the documentation states that #buttons don't trigger the submit handler. Do we change the documentation or update the API to meet what seems to be the original intent?
Comment #38
larowlantbh I'm not sure, I don't use #type button very often
Comment #39
seanbI don't think #9 fixes the issue. The problem is in the browser. When pressing the enter key, the first input element with type "submit" is triggered, instead of the actual form submit button. We should really stop using type "submit" for AJAX buttons, since they are not the primary form submit button. This is caused by the fact that
Drupal\Core\Render\Element\Buttonalways uses "submit" as the button type.So we either need to add a new setting to allow changing the button type, maybe a key like
#submit_button?Or another render element that always uses the button type. Something like
#type = 'ajax_button'(although it is not necessarily always going to be used for AJAX stuff).Comment #40
smustgrave commentedDidn't see a patch to review
But was previously tagged for issue summary update which I'm sure is still needed. Plus when a patch is there will need a test case
Comment #41
darvanenThanks @smustgrave :)
#39: @seanB it would seem at the moment that Drupal's take on an AJAX button is that its sole purpose is to trigger a backend handler on the form, be it the submit handler or a different method (and therefore the advice to simply turn off the submit handler).
How do we make the case for buttons which don't send anything to the backend at all? I think that would be the easiest one to get support behind. And from there we can talk about how to do it.
I've lost track of what I was trying to do when I came across this issue.
Perhaps we could make a contrib module which demonstrates the issue and will give us a measurable indicator of demand (beyond how few concerned people have found this ticket)?
Comment #42
seanbWell, the list of issues in #31 is a pretty big indicator that people are struggling with problems because of the fact that it is impossible to create an
<input type="button">at the moment. Of course, we could do this in contrib, but that would mean all modules that have AJAX buttons would need a dependency on this new module to solve the issue.Adding a flag like
#submit_buttonto the existing button element would probably be the easiest and most developer friendly way to solve this. So I'm hoping this is a thing we could add to core.Comment #43
smustgrave commentedPossible to get the issue summary update or a test case?
Comment #44
catchI think #submit_button defaulting to TRUE is a good idea here, it would be backwards compatible and less confusing than having two button elements.
This is a small API addition rather than a bug IMO, so reclassifying as a task. Also re-titled and updated the issue summary.
Comment #45
oily commentedComment #46
oily commentedComment #47
oily commentedComment #48
oily commentedI am testing in the browser via the Drupal UI the current 'fix' now committed to the issue fork. I am testing using the 'simple form' in the examples project forms api module.
With the #submit_button left at default TRUE the html is:
<input class="button--primary button js-form-submit form-submit" data-drupal-selector="edit-submit" type=“submit” id="edit-submit" name="op" value="Do Not Submit!">With the #submit_button set to FALSE:
<input class="button--primary button js-form-submit form-submit" data-drupal-selector="edit-submit" type="button" id="edit-submit" name="op" value="Do Not Submit!">Note that the class attributes 'js-form-submit' and 'form-submit' are left unchanged. The 'type' attribute IS changed which seems the only requirement for the purposes of this issue. If anyone sees a good reason to change the class attributes please comment!
Otherwise the task of writing a failing functional test should be straightforward.
Comment #50
oily commentedThe Forms functional tests are extensive and it is tricky to work out the best place to put this test. I have arrived at the idea of creating one or two new test forms with only 2 or 3 form elements defined. Then insert a new test function (or 2) in the ElementTest test class and assert that the 'type' attribute contains 'button' or 'submit' using the 'xpath' selector type..
Comment #51
oily commentedThe test created under this ticket ElementTest::testSubmitButtonAttribute is passing.
Comment #52
smustgrave commentedLeft comments on MR.
Comment #53
oily commentedFor some reason my local phpunit is ignoring the functional test method introduced by the merge request. Troubleshooting this now. phpunit was executing the test method fine until the most recent batch of commits.
Not sure maybe my local phpunit config is wrong. I mean if the gitlab pipeline is passing the phpunit functional tests, that is canonical?..
Okay sussed it out: running 'vendor/bin/phpunit -c core --filter=testSubmitButtonAttribute' does not work.
'vendor/bin/phpunit -c core --filter=testFormElements' works. It is the correct command to use locally. But it just happens to be the way the tests are (intentionally) configured. Nothing amiss.
Comment #54
oily commentedComment #55
darvanenI'm so happy to see some traction on this and buy-in from @catch. The approach looks good to me, I think we just need to tidy a few things to grease the wheels.
Comment #56
oily commented@darvanen Your suggestions look good.
Comment #57
oily commentedComment #58
smustgrave commentedThreads about the 80 character wrapping still seem to be needed.
Comment #59
oily commented@smustgrave @darvanen I have reacted to the code comments re the 80 character limit. Changing back to Needs review
Comment #60
oily commentedComment #61
oily commentedComment #62
oily commentedComment #63
oily commentedComment #64
smustgrave commentedShows the test coverage
Also resolved the threads because they appeared to all be addressed.
I did not find anything else pending and code looks fine to me.
Going to mark it.
Comment #65
quietone commentedI read the IS, comments and the MR. I think two comments need to be improved. I did one but the other I wasn't sure what to add so setting this to needs work for those comments.
Comment #66
oily commentedReplaced comments with suggestion of @trackleft2.
Rebased.
Comment #67
oily commentedComment #68
smustgrave commentedBelieve feedback has been addressed
Comment #69
catchI think this could use a change record for the small API addition.
Comment #70
smustgrave commentedAll done https://www.drupal.org/node/3505842
Comment #71
quietone commentedcatch asked me to check in here.
Thanks for improving the comments! It is much clearer and easier for developers to understand how to use this. The description for '#submit-button' doesn't follow our practice, where the default is at the end. For example, 'When FALSE this happens. Defaults to TRUE'. But it is not a standard and more importantly, the information here is correct. That should not delay this improvement.
Leaving at RTBC
Comment #74
catchCommitted/pushed to 11.x, thanks! Also cherry-picked to 10.5.x for API parity.
Comment #77
boulaffasae commented.