Problem/Motivation
The Options module uses _none to indicate the empty option. However, for browsers this means the select element has a value. As a result, common client side logic no longer applies. For example:
- Client side validation: #1585554: Validating lists (select-fields) not working correctly
- HTML5 validation: #2942806: HTML5 Validation not working for Field type List
MDN example of an option suggests using an empty string for the value attribute, see https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/select
Proposed resolution
- Map the _none option label to the Select #empty_option in a process callback
- Set
#empty_value = ''in a process callback, so the select renders as<option value="">#empty_option</option>(BC break) - Deprecate the
_noneoption in #2448545: Modernize form select option helpers
Remaining tasks
- Write a merge request
- Review
- Commit
User interface changes
None
API changes
A select element now uses an empty string for the empty option:
Before:
<option value="_none">- Select -</option>
After:
<option value="">- Select -</option>
Original report by attiks
This was originally introduced in #735426: Fields with select widget: first option is selected by default even if no 'default value' set for the field
A user of clientside_validation reported this bug (#1585554: Validating lists (select-fields) not working correctly), it is caused by the fact that the options.module uses the following: $options = array('_none' => $label).
First problem is that the outputted HTML contains <option value="_none"> and this means the select element has a value.
Second (unrelated) problem is that we allow people to enter "_none|oops" as an allowed value, but it will never be outputted unless a default value is selected.
I found #735426: Fields with select widget: first option is selected by default even if no 'default value' set for the field where this was introduced, so I would like to change the use of '_none' to NULL
| Comment | File | Size | Author |
|---|---|---|---|
| #110 | D11x3-1585930-110.patch | 9.94 KB | joseph.olstad |
Issue fork drupal-1585930
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:
- 1585930-options-none
changes, plain diff MR !13034
- 1585930-options-module-uses
compare
Comments
Comment #1
attiks commentedPatch attached
Comment #2
geerlingguy commentedCan't see why FAPI options uses '_none' when NULL seems a more logical (and less painful) method of adding an empty option. +1 here.
Comment #3
swentel commentedI can't think of any good reason why '_none' is used either. Rerolled the patch as it didn't apply anymore.
Assigning to yched however because maybe he has some more historical background, I'll let him push this RTBC or not.
Comment #5
yched commentedOuch - bad memories around this, i'm not too thrilled to poke in there :-).
This probably had to do with what FAPI select / radios / checkboxes allowed back in D6 / D7. Maybe those elements are fine with a NULL / empty string value now, but that would need to be thoroughly tested.
Also
- patch uses NULL as value in the FAPI code but empty strings in submitted forms values in tests ? Not ideal, but I guess that's how it works...
- could we settle on '' (single quotes) rather than "" (double quotes) for empty string ?
Comment #6
haydeniv commentedWe're dealing with this over at Select or Other #1830090: Align with Drupal core select list, NULL (-None-) value. as well, so we're anxious to see where this one goes.
Comment #7
brad.bulger commentedan array key should be an empty string, since PHP will cast NULL to one anyway.
i'd like to see this change too. another problem with how it is now is that you can create your own explicit empty-string key|label pair - eg "|None" - in the list of values for the field, but users end up seeing both the Drupal-generated "_none|N/A" and your "|None", both of which produce the same end result.
this already was an empty string up through 6.x, so presumably somebody changed that to '_none' for some reason. it would probably be good to know why that was done.
i'd like to also suggest allowing users to more easily customize the option_none value's label by declaring their own explicitly in the field's value list, as with "|None". that would work if we just swap the way the Drupal-generated value is added to the option list in _options_get_options():
instead of
Comment #8
rwohlebIn D7, I find it odd that the options module doesn't just pass #empty_value and #empty_option to the underlying select, but instead does its own thing with theme_options_none(). Wouldn't it make more sense to set these standard FAPI options from the field settings instead? At least this way it would be done in a way people would expect, plus #empty_value defaults to an empty string (can't be NULL according to docs), so the question of "" or "_none" seems handled.
In D8 this also seems to apply, but I'm not up-to-date with D8 development.
Comment #9
haydeniv commentedIn 8 you implement a isEmtpy() method I believe. https://drupal.org/node/1985716
I'm going to take a crack at converting this module to 8.x pretty soon. Unless someone beats me to it of course.
Comment #10
brad.bulger commentedwhat is this issue waiting on? does it have to be fixed in 8.x and then backported to 7.x?
Comment #11
swentel commentedreroll
Comment #13
damienmckennaThis affects the drupal.org "gender" profile field on d.o, so I'm marking it as such. As a result, I'm also closing a duplicate: #2412393: "Prefer not to share" gender option option does not display correct in profile form.
Comment #14
damienmckennaTaking it off yched's plate as it hasn't been touched in a year.
Comment #15
yesct commentedThis could use an updated issue summary to make it easier for someone to help with this. See instructions: https://drupal.org/contributor-tasks/write-issue-summary
Comment #16
tstoecklerYes, #8 is spot on. Let's use #empty_option and #empty_value please!
Comment #17
xjmComment #20
kenorb commentedIn Drupal 7 pretty weird is happening.
I've got field like:
where before I start the form, the value is string, when submitting, it's integer. When it's integer, this code doesn't make any sense:
When you comparing integer with string, string becomes integer (zero in this case). So in this case #value equals to '_none', but it's not really none. I don't know if it's my custom code causing it, but probably not.
Comment #21
imot3k commentedRe-rolled against 8.2.x.
Comment #22
imot3k commentedComment #23
imot3k commentedRe-roll.
Comment #24
imot3k commentedThe last patch threw an error when trying to submit a form with a select list that is not required.
Comment #25
geerlingguy commentedComment #30
rgpublicOne question on #24: Are there any inherent problems still associated with the patch? Or are the tests only failing because the tests themselves have to be adapted? TIA!
Comment #31
mpp commentedThe HTML
required Attribute only triggers client-side validation if the value isn't set so we should implement this issue and remove '_none'. See https://www.w3schools.com/tags/att_select_required.asp
The patch in #24 no longer applies to 8.3.5
Comment #33
rgpublicI agree, mpp. Client-side validation doesnt work if there's a "_none" value. Furthermore, you'll get W3C validation errors if there's not a decent empty value. TBH, I think it was an extremely bad idea to introduce this magic _none value. It ought to be removed ASAP. Hopefully, this patch is included in future Drupal versions.
The patch, BTW, doesnt apply, because the test files dont seem to exist anymore and - as usual - the Array syntax has changed to just brackets. I've added a patch for 8.3.x. Perhaps it will also apply for later versions.
Comment #34
anas_maw commentedPatch #33 solved the issue for me Drupal core 8.4.4, this should be committed to core
Comment #35
anas_maw commentedThis should be set to RTCB
Comment #36
wim leersThe patch does not apply.
Comment #37
arunkumarkI have re-rolled the patch for supporting the latest version of Drupal 8.5.x.
Comment #39
jofitzRemoved Needs Reroll tag.
Comment #41
nikunjkotechaUpdated tests to check for empty string instead of _none.
Comment #43
nikunjkotechaThis should make the unit tests happy.
Comment #44
chiranjeeb2410 commentedPatch applies cleanly. Usage of empty strings seems alright instead of _none.
RTBC good to go!
Comment #45
larowlanI'm not sure this is the same thing.
$element[#value] of FALSE or possibly 0 would match here.
I think we need === not ==
we're being strict about type here, so that makes me more sure ^ should be strict too
I think we should use a constant here instead of ''.
OptionsWidgetBase::EMPTY_VALUE or something.
That way, if we change it in the future, we do it once
Would bring it in line with #2448545: Modernize form select option helpers too
same comment about strictness applies here
Comment #46
nikunjkotechaThanks @larowlan for quick feedback.
Fixed the issues related to strict checking.
About replacing '_none' or '' with class constant - I would request to keep it in #2448545: Convert '_none' option to a constant and deprecate form_select_options() only as it will be out of scope for current ticket + changes required for #2448545 are even outside the Options core module. There are many references to '_none' outside the module so it makes more sense to create the constant in core itself (outside the module). Approach and solution in #2448545 look good to me (and for sure it is going through it's own review cycle), only issue I see is bit of rework required in one of the patches after the other patch is merged, which should be fine IMO.
Comment #47
amateescu commentedWe should change
OptionsSelectWidgetto use the#empty_optionand#empty_valueproperties of the select FAPI element (\Drupal\Core\Render\Element\Select), like already mentioned in #8 and #16 :)Comment #48
amateescu commentedAlso, this is kind of a BC break for modules which implement
hook_options_list_alter()so we need a CR at least.Comment #49
idebr commentedClosed #2942806: HTML5 Validation not working for Field type List as a duplicate issue.
Comment #51
colanAdding the other issue as related.
(Although, personally, I don't see the point of continuing to maintain this module in core, when Taxonomy always seems to be the better choice. But maybe I'm missing something?)
Comment #55
Hammad Ghani commentedPatch applied successfully for 8.8.4.
Comment #56
joseph.olstadrerolled patch 46
Comment #58
ridhimaabrol24 commentedRerolled patch for latest head for 9.1.x
Comment #59
anushrikumari commentedComment #60
ridhimaabrol24 commented@anushrikumari The test cases are failing. hence the correct status is "Needs Work".
Comment #61
ridhimaabrol24 commentedFixing tests!
Comment #62
ridhimaabrol24 commentedFixing failed test.
Comment #63
andrewmacpherson commentedRe: #51. Emphasis mine...
That's a fairly opinionated choice; I'd tend to disagree here. Which one you choose really depends on your needs.
Taxonomy is a good choice if you need:
On the other hand, Taxonomy module brings in a lot of extra baggage, if all you want is a simple enumerated property on a node.
taxonomy/term/%route of their own. Unless your "options" need a page of their own, then you'll need to take steps to hide this from site visitors.Comment #65
villette commentedReroll patch #62 against head of 9.2.x
Also applies to 9.1.0
Comment #66
longwaveI'm confused between this and #2448545: Modernize form select option helpers as they both seem to be going in the same direction but the patches are quite different.
I don't believe we can just change this constant as there could be JavaScript or custom code depending on the existing special value. However, I also don't know how we would be able to deprecate it.
Comment #67
geek-merlin> I don't believe we can just change this constant as there could be JavaScript or custom code depending on the existing special value.
Yes good point.
> However, I also don't know how we would be able to deprecate it.
We can use the "Only new installs get it" pattern.
A imho thorough solution would make this a parameter of the render array (like '#empty_key'), which defaults to '_none' for old installs, and '' for new ones. (Which also means i'd suggest to close the other issue as dup.)
Comment #68
joseph.olstadpatch 64 needed a reroll
Comment #71
nikitagupta commentedRerolled patch #68.
Comment #72
nikitagupta commentedfixed the failed test case.
Comment #74
joachim commentedNeeds work based on #67. This is going to break any custom code that uses form options, such as submit and validation handlers.
The BC policy says that the form and render API are public, though individual pages and forms aren't. That means that the value you get from a form element is part of our public API.
Comment #75
clayfreemanRe: #67
The problem with "new installs get it" is that you effectively prevent people with centralized custom modules from doing new installs without a major refactor, or manipulating state somewhere.
I'd tend to lean toward a new element being the safest option, as no BC concerns present themselves.
Comment #76
joachim commented> A imho thorough solution would make this a parameter of the render array (like '#empty_key'), which defaults to '_none' for old installs, and '' for new ones. (Which also means i'd suggest to close the other issue as dup.)
That would make it different for each form, which is probably needed in case a site has a mixture of contrib modules that have updated to the new empty key and modules that haven't.
> The problem with "new installs get it" is that you effectively prevent people with centralized custom modules from doing new installs without a major refactor, or manipulating state somewhere.
But could we also set a global setting in an update hook, so existing sites don't need to do anything? A container parameter would do it, but I don't think there's a way that could be set permanently by an update hook?
Comment #77
johnpitcairn commentedFWIW, the patch at #72 also applies against 9.2.x and works as expected with clientside validation. Having html5 validation for list elements is a huge win.
Comment #78
johnpitcairn commentedPatch at #72 no longer applies to 9.2.7. I'll possibly attempt a backport sometime soon, unless anyone else cares to do so.
Comment #79
ankithashettyRerolled the patch in #72 as requested in #78. Retaining status as "Needs work" for the reviews mentioned in #74 to #77.
Thanks!
Comment #84
mistrae commentedRerolled against latest version
Comment #85
dhinakaran commentedRerolled against the patch 158590_55 for version 9.x
Comment #86
johnpitcairn commentedComment #87
arunkumarkThe patch has been rerolled for Drupal 10.1.x
Comment #89
joseph.olstadPatch 84 still applies cleanly to the HEAD of 9.5.x, 10.0.x and 10.1.x, I just checked vs the latest in each branch.
Patch 84 passes.
Patch 85 and 87 were not needed and were incorrectly rolled and fail testing.
https://www.drupal.org/docs/develop/git/using-git-to-contribute-to-drupa...
Comment #90
longwaveWe still need to address the backward compatibility concerns from #66 onwards: we need a way to allow keep
'_none'for existing sites that depend on it, while allowing''for sites that want to use that instead.Comment #92
joseph.olstadpatch 84 no longer applies to 10.0.x, will need a new patch for 10.0.10
Comment #93
joseph.olstadComment #94
sumithra ramalingam commentedThank you @joseph.olstad
Patch #93 working clearly with D10.1.4 version.
Comment #95
luenemannFixed link to project page.
Comment #96
darktek commentedNew 11.2.3 version was released today and patch #93 is not working anymore.
I'm sharing this new patch
Comment #97
eduardo morales alberti@darktek some changes do not match the 93 patch:
Why do you replaced "$element['#value'] === ''" by "$element['#value'] == ''" or "if ($value !== '') {" by "if ($value != '') {"?
interdiff 1585930-93.patch 1585930-96.patch
Comment #98
eduardo morales albertiWe will open a MR to track the changes properly
Comment #100
eduardo morales albertiComment #101
eduardo morales albertiFailed PHPUnit:
Functional with Javascript:
Comment #102
eduardo morales albertiComment #103
eduardo morales albertiFailing:
Comment #104
idebr commentedTo minimize the BC break, I suggest we map the
_noneoption to the Select#empty_optionand set#empty_valueto an empty string in a process callback.This implementation allows existing options to remain unchanged; only client side logic is impacted (eg. JavaScript)
Comment #106
idebr commentedBased on the issue summary, comments and related issues the immediate problem is limited to single select elements and client side validation. If I missed something, let me know and I can update the issue accordingly
Refactoring the
_nonevalue itself is being worked on in #2448545: Modernize form select option helpersComment #107
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #108
idebr commentedMR updated after #3513120: Fix LongLineDeclaration in Functional tests was commited
Comment #109
smustgrave commentedStill appears this will need a CR. Do have some backwards compatibility concerns but not sure how to address them
Comment #110
joseph.olstad