sveltekit-superforms icon indicating copy to clipboard operation
sveltekit-superforms copied to clipboard

Using superForm with $derived

Open unlocomqx opened this issue 1 year ago • 12 comments

  • [x] Before posting an issue, read the FAQ and search the previous issues.

Description I tried retuning multiple superforms in an array, but I could not use it in the frontend. The only convenient way was to destructure each superform in the each tag but svelte doesn't support "declaring" stores out of the top-level of the component

If applicable, a MRE https://www.sveltelab.dev/9tdm9ul193m3tz5

unlocomqx avatar Apr 05 '24 14:04 unlocomqx

It didn't occur to me to extract the form into a separate component 🤦 So this would work

{#each superforms as superform}
	<Comp {superform} />
{/each}

Closing.

unlocomqx avatar Apr 05 '24 16:04 unlocomqx

I'm reopening this issue because I encountered a problem

When I write this code

<script>
const superforms = data.my_forms.map((form) =>
	superForm(form, {
		dataType: 'json'
	})
);
</script>
{#each superforms as superform (superform.formId)}
	<ConditionItem {superform} />
{/each}

It works well but when a new form is added, then that's where I run into a problem

If I try to use $derived

const superforms = $derived(
	data.condition_forms.map((form) =>
		superForm(form, {
			dataType: 'json'
		})
	)
);

I get this error

Uncaught Error: onDestroy can only be used during component initialisation.

Repro https://www.sveltelab.dev/9tdm9ul193m3tz5

  • Click the Add button to add a new item (thus a new form)

unlocomqx avatar Apr 06 '24 19:04 unlocomqx

I found a way to fix it, I pushed my code here

If you'd like, I can make a PR for this

Cheers

unlocomqx avatar Apr 07 '24 01:04 unlocomqx

A PR for this would be very nice!

ciscoheat avatar Apr 07 '24 21:04 ciscoheat

Okay, I will send a PR for this

unlocomqx avatar Apr 07 '24 22:04 unlocomqx

The problem is bigger than I anticipated, it'll require an overhaul of the onDestroy/cleanup handling. It'll have to wait until later. For now, calling superForm within $derived is not possible.

ciscoheat avatar Apr 23 '24 12:04 ciscoheat

Okay, it will be good to have dynamic forms in the future. I will try to find a workaround for now.

unlocomqx avatar Apr 23 '24 14:04 unlocomqx

Out of curiosity, what is the proscribed workaround for this? Just not to use superforms for an array of forms? Seems common to have a list of things, then have to a button to delete them individually...etc. Are folks just making client side individual API posts here for now?

snide avatar Jul 20 '24 23:07 snide

There's an example here

How to use Superforms with actions on a list of data.

Although, I prefer not using the formId and instead get the submitter and alter the submitted data based on its props.

I also only submit tainted rows as I only need to save the text fields that the user modified.

Sample code below

const {
	....
} = superForm(data.fields_form, {
	dataType: 'json',
	resetForm: false,
	onSubmit({ submitter, jsonData }) {
		const action = submitter?.getAttribute('name') || 'update'
		const action_value = submitter?.getAttribute('value') || ''
		jsonData({
			...$form,
			action,
			action_value,
			fields: Object.fromEntries(
				Object.values($form.fields)
					.filter((f) =>
						Object.keys($tainted?.fields ?? {}).includes(f.id.toString()),
					)
					.map((f) => [ f.id, f ]),
			),
		})
	},
})

unlocomqx avatar Jul 20 '24 23:07 unlocomqx

Terribly embarrassed to say I missed that entire examples page. You made my day. TY!

snide avatar Jul 20 '24 23:07 snide

Am I correct in assuming that this means that in Svelte 5 pre-filled forms will never update after submitting as the invalidated load function reruns but the form wont update unless wrapped in $derived, which isn't possible either?

So the only way then to use pre-filled forms in Svelte 5 seems to be to use

    invalidateAll: false,
    resetForm: false,

MerlinB avatar Oct 18 '24 08:10 MerlinB

As long as you don't call superForm in $derived, it should work fine. A workaround for the array example is to call superForm for each data in a component instead, sending the SuperValidated form data as a prop:

{#each data.my_forms as form}
  <ConditionForm {form} />
{/each}

See the componentization guide for more info.

ciscoheat avatar Oct 18 '24 08:10 ciscoheat