-
-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use:enhance
resets form even if form.x
values used for populating it
#8513
Comments
I would argue that server gets final say - matches non-JS behaviour, and I think submission of a form doesn't mean that the client should automatically clear it. Definitely a breaking change, however the |
|
The input also does not reset as long as a new or different value is submitted. However, submitting the same value consecutively guarantees a reset. https://stackblitz.com/edit/github-akyafk?file=src/routes/+page.svelte The issue is |
I came here just now to post a very similar issue with Instead of initializing the form from The result is the same as in your repro case: the form behaves differently with |
This is a good point - and a reason why my idea about not calling reset when |
If it's not possible to detect this at the framework level, could we potentially provide (or document) a custom It could look something like this in userland (though with this version, there's no way to opt out of <script lang="ts">
import { enhance } from '$app/forms';
import type { SubmitFunction } from '$app/forms';
function noResetEnhance(node: HTMLFormElement, cb?: SubmitFunction) {
return enhance(node, async (initProps) => {
const returnedCb = await cb?.(initProps);
return (returnProps) => {
returnedCb?.(returnProps);
returnProps.update({ reset: false });
};
});
}
</script> |
Maybe I'm stating the obvious, but an ideal solution would replicate non-enhanced behavior--it would not try to avoid resetting the form, but instead reset it and then repopulate it using whatever logic populated it in the first place. |
I've pushed an update to my reproducible case. It demonstrates that not only does the form not behave the same with/without My script has this: export let data;
let city = data.city; And I changed one part of the page content—outside the form—from: <p>City is now {data.city}</p> to <p>City is now {city}</p> After submitting the form with This will be a real puzzler to solve, if it's even possible. |
You’ll need to use a reactive statement such as: The $: declaration causes city to be updated anytime data.city changes |
@s3812497 Good point. The reactive statement does work around the issue. I’d still consider it either a bug or maybe just a limitation to be documented, because the page behavior with vs. without |
Update: oh yeah, I remember there is a reason I didn't use If your goal is to initialize <script>
import { enhance } from '$app/forms';
export let data;
$: city = data.city;
</script>
<form use:enhance>
<input name="city" bind:value={city} type="text" />
<input type="submit" />
</form> The behavior is no characters appear when you type into the city field. |
Ah yeah, sorry. I was thinking of the one-way binding with value={city} |
One idea I had to fix the OP's issue is to do this after for (const element of form.elements) {
if (
(element instanceof HTMLInputElement && element.type !== 'submit') ||
element instanceof HTMLTextAreaElement ||
element instanceof HTMLSelectElement
) {
console.log('dispatch on', element.value);
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
}
} ... but the input values are still lost if I submit the form twice in a raw without changing the values inbetween. Svelte rightfully says "hey from my perspective the value hasn't changed" so it doesn't run an update, things get out of sync and eventually the input value is lost. |
I ran into this issue today as well. FWIW, part of what I expect to be enhanced in a form by adding "use:enhance" vs a typical browser form submit is that my form fields won't be cleared. Since the page is not reloaded, page state should be preserved. |
FWIW this was not reset by default originally, but many people complained that this doesn't match the native browser behavior, which is why we changed it but also added the option so that you can disable the reset. |
This seems to fix it — it's a tiny bit hacky but probably acceptable under the circumstances? // client.js
const props = {
- form: result.data,
+ form: null,
page: { ...page, form: result.data, status: result.status }
};
root.$set(props);
+root.$set({ form: result.data }); |
Could this have any unintended side effects like reactive statements running twice? I don't think so because it's batched for the next microtask. |
Reactive statements will run twice, but they need to be accounting for the |
Marking this |
I am not sure if this should be handled better in svelte. |
@PatrickG Any chance you could just post the relevant content here? Not everyone is on Discord. Thanks. |
The problem is, when svelte rerenders the value, it does not set the value of the input, because it thinks it has not changed, but the You can see it in the JS output on line 41 of this repl: https://svelte.dev/repl/40820395c601425aa27bed26d628c799?version=3.55.1 if (dirty & /*form*/ 1 && input_value_value !== (input_value_value = /*form*/ ctx[0]?.providedName || '') && input.value !== input_value_value) {
input.value = input_value_value;
} would need to be if (dirty & /*form*/ 1 && (input_value_value !== (input_value_value = /*form*/ ctx[0]?.providedName || '') || input.value !== input_value_value)) {
input.value = input_value_value;
} or without the cache if (dirty & /*form*/ 1 && input.value !== (ctx[0]?.providedName || '')) {
input.value = ctx[0]?.providedName || '';
} Normally svelte philosophy is "don't mess with the dom yourself". From this standpoint, it is correct to not set input.value again. BTW, you can workaround this with a two-way binding if (dirty & /*providedName*/ 1 && input.value !== /*providedName*/ ctx[0]) {
set_input_value(input, /*providedName*/ ctx[0]);
} |
@BMorearty did you ever figure out a solution to this? I just ran into this same issue and I haven't found an easy way to repopulate the form with data returned by my |
@stv8 I came up with three workarounds. These are the notes I wrote to myself at the time:
<script>
import { enhance } from '$app/forms';
export let data;
// Initial assignment on page load and reassignment on change
$: city = data.city;
</script>
<p>City is now {city}</p>
<form method="POST" use:enhance>
Change city to
<!-- Can’t use bind:value here -->
<input name="city" value={city} type="text" />
<input type="submit" />
</form>
<script>
import { enhance } from '$app/forms';
export let data;
// Initial assignment on page load
let city = data.city;
</script>
<p>City is now {city}</p>
<form
method="POST"
use:enhance={() => {
return async ({ update }) => {
await update();
// Redo the assignment from the script above
city = data.city;
};
}}>
Change city to
<input name="city" value={city} type="text" />
<input type="submit" />
</form> |
@BMorearty thanks, I thought option 3 was going to work for me but I'm still having issues. I think I'm going to go ahead and try out https://superforms.vercel.app instead. |
I hope this behavior is documented somewhere. I was having a hard time understanding why the <!-- src/routes/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import { tick } from 'svelte';
export let form;
</script>
<form
use:enhance={() => {
return async ({ update }) => {
await update();
console.log(form); // null
await tick();
console.log(form); // { at: 1705498579836 }
};
}}
method="post"
>
<button>Submit</button>
</form> // src/routes/+page.server.ts
export const actions = {
default: () => ({ at: Date.now() })
}; Current documentation is misleading, since
Update: |
was this fixed? I still have the problem. Should I use |
My understanding is the form will take any values that are set by the server; if I remove
use:enhance
from the form in this repo, I get the same values after POST that I do before. But addinguse:enhance
changes this behaviour. What am I missing here?Originally posted by @angrytongan in #8404 (comment)
Not sure what's the best way to respond here. One way would be to check the
form
value that is returned, and if it contains any keys, assume it may be used for form data, and don't reset in that case. It's an imperfect solution but probably the best we can do. (It's also a confusing solution maybe, so maybe it's better to not reset after all..? could be seen as a breaking change though)The text was updated successfully, but these errors were encountered: