Fix x-bind:value on <option> losing attribute when value is an empty string#4831
Merged
Merged
Conversation
3 tasks
Contributor
|
😳 wow, @joshhanley ... how did you find this so quickly? Amazing work! |
Collaborator
Author
|
Thanks! |
Instead of reimplementing attribute set/remove logic inline, delegate to the existing bindAttribute function which already handles null/undefined/false removal and setIfChanged. Also reverts the unrelated isObjectAttr removal to keep the diff focused on the option fix. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The Scenario
When using
x-bind:valueon an<option>element insidex-forand the bound value is an empty string, the rendered<option>is missing itsvalueattribute. The option's.valueproperty then reflects whatever text contentx-textwrites into it, so a form submission for the "empty" option sends the option's label instead of"".The first option ends up as
<option>Empty</option>(novalueattribute), andoptionEl.value === 'Empty', not''.Adding any text inside the
<option>template (e.g. ) masks the bug, which was the clue in the original report.The Problem
bindInputValueinpackages/alpinejs/src/utils/bind.jshas an early-return optimisation:For most elements,
el.valueis anchored to thevalueattribute, so this short-circuit is safe. For<option>it isn't: per the HTML spec, when an<option>has novalueattribute, its.valueproperty reflects the element's text content.Because
bindruns beforetextin Alpine's directive order, the first pass sees an option with no text yet (el.value === ''), the bound value is also'', and the function returns early without setting the attribute.x-textthen writes"Empty"into the option, andel.valuesilently becomes"Empty"because there's novalueattribute holding it to"".The Solution
Special-case
<option>inbindInputValueso thevalueattribute is written or removed deterministically:For non-nullish values, the attribute is set explicitly via
setIfChanged. This anchors.valueso it can't drift when sibling directives write text.For
null/undefined/false, the attribute is removed, matching the conventionbindAttributealready uses for other attributes. This preserves the DOM's text-as-value fallback, so:value="someUndefinedKey"opts back into label-as-value behaviour.The change is scoped to
<option>and leaves the text/checkbox/radio/select paths untouched.Fixes #4830