sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
80.2k stars 4.27k forks source link

[Feature]: Support Default Values in Form Controls #9230

Open ITenthusiasm opened 1 year ago

ITenthusiasm commented 1 year ago

Describe the problem

Unfortunately, as of today, Svelte does not support default values[^1] for form controls. For example, Svelte renders

<input value="default-value" />

as

<input />
<textarea>default text</textarea>

as

<textarea></textarea>

and

<select>
  <option>1</option>
  <option selected>2</option>
</select>

as

<select>
  <option value="1">1</option>
  <option value="2">2</option>
</select>

(It's also a little odd that the value attribute is added here when it wasn't specified in the HTML. But for now, I'm only interested in the missing selected attribute.)

This dissonance between Svelte and HTML results in an unintuitive experience for developers intending to use Svelte as a superset of HTML, and it acts a roadblock to important features in forms. One example is that this behavior prevents form resets from behaving correctly, as pointed out in #8220. Another example is that it prevents developers from using the defaultValue/defaultChecked/defaultSelected attributes of fields to identify "dirty controls" -- a common use case in forms.

When using native HTML/CSS/JS, these default* properties can be used to check which fields are dirty. The same code works just fine in Solid.js and Vue. (It's worth noting that neither Vue nor Solid add the value attribute to the options if it wasn't specified.) Amazingly, React almost completely supports this use case as well. Only select elements don't work because they don't support the selected attribute currently. So I was a little surprised to see that Svelte doesn't support this use case at all -- not even for inputs. (If you see things turn red in Svelte's demo, it's because JS is comparing field values with non-existing default values the entire time, not with anything that's an actual default value. So everything stays red -- except the checkbox, for obvious reasons.)

It would be fantastic if Svelte could support default values. I would even be bold enough to say that this is a necessary feature (since not having the feature is technically a regression from browser behavior).

[^1]: It seems that svelte uses JS to simulate giving a form control a default value. However, this is not a "true" default value. It's more like an onMount side effect that still leaves the developer without the browser's features surrounding default values. Hope that isn't too confusing.

Describe the proposed solution

During SSR and CSR, Svelte should render all form controls with the exact markup that they're given. That way, default values will be supported out of the box, and no attributes will unexpectedly disappear from (or force themselves upon) form controls.

Alternatives considered

Using state is a possible way around this problem, but it's more complicated and likely less performant for obvious reasons. Besides that, I don't think there's an alternative solution to preserving the developer's intended markup for form fields.

Importance

I cannot use Svelte without it

ITenthusiasm commented 1 year ago

Update: It might actually be difficult for Svelte to implement this change simply by letting attributes like value behave as normal. This is because almost certainly, there are several applications out there using bind:value -- unaware that the code that's being written actually diverges from what normal HTML would do/expect.

Svelte could do it, but it would be a significant breaking change, and it would leave people without the ability to use bind:value. (Though, in my opinion, bind:value could easily be replaced with pure JS.) Perhaps a better alternative would be for Svelte to identify all of the areas in which it deviates from pure HTML. Then, in those moments of deviation, it could perhaps introduce something like a native:* directive? That way, the developer can express that they just want the pure attribute there; they don't want any JS magic. For instance, a developer could use native:value="default-value" or native:checked or native:selected.

I think Svelte should be able to support the native default value use case for textareas without causing a problematic breaking change. Developers would still need to use bind:value if they wanted data binding for that element anyway.


Note: Somehow Vue seems to have found a way to support both the JS magic and the native default value use cases individually? I don't really know how they've done it. Either it's intentional, or there's a bug somewhere. But if it's intentional and there's a desire to avoid introducing new syntax like native:*, then maybe Vue's approach would be worth exploring?

dummdidumm commented 8 months ago

What we can do is special-case the "value was set statically" and in this case don't clear the value. What we cannot do is do that when the value is set through a variable. In that case we don't know whether or not the initial value should be the one that's the default value - and in fact, both Solid and Vue behave that way. Maybe it's intentional, maybe it's by accident, but both don't set the initial value as the default value when you pass a dynamic variable to for example value in <input>. Update: In fact, this is how it works already now in Svelte 5. We just need a test to ensure we don't accidentally regress there.

ITenthusiasm commented 8 months ago

In that case we don't know whether or not the initial value should be the one that's the default value - and in fact, both Solid and Vue behave that way.

Ah, I was wondering what was causing that discrepancy in Vue. That's helpful! Thanks for identifying that!

In fact, this is how it works already now in Svelte 5. We just need a test to ensure we don't accidentally regress there.

That's encouraging to hear. I'm looking forward to Svelte 5's release then. 🙏🏾

What we cannot do is do that when the value is set through a variable.

That is an interesting dilemma. In most cases, I don't think that this is a big deal. Being able to use default values at all is a huge win. But there might be some edge case concerns. Is this limitation on the client side only or also during SSR?


(Also, thanks for taking the time to consider/review this issue.)

dummdidumm commented 1 month ago

Linking some findings from a related PR: #10617

dummdidumm commented 2 weeks ago

Investigated this more. Here are my findings:

There are also questions around what to do if defaultValue and value is falsy - should the default value take precedence in that case, or not? And should it differ between whether or not you bind: to the value?