Skip to content

Remote Functions: declarative values for edit forms #15566

@paoloricciuti

Description

@paoloricciuti

Describe the problem

Right now, the API to set values in the form field is imperative, meaning the way to do it is

my_form.fields.set({})
// or
my_form.fields.name.set("My Name")

this makes creating an "edit" form where the values should be initialized with the current values very tricky to do, especially if you want to do it also during SSR to allow for progressive enhancement and especially if you need to have an array of elements. The only somewhat decent solution I found is to create a form.for inside a derived and set the value there

<script>
	import { get_things, add_thing, edit_thing } from './things.remote.ts';
	import { untrack } from "svelte";

	let things = $derived(await get_things());
	let thing_forms = $derived.by(()=>{
		const forms = [];
		for(let thing of things){
			const edit = edit_thing.for(thing.id);
			// we need to untrack because we are setting state in a derived
			// which is a bad practice in svelte
			untrack(()=>{
				edit.fields.set(thing);
			});
			forms.push({
				id: thing.id,
				edit
			});
		}
		return forms;
	});
</script>

this is also currently affected by this bug #15465 which I think we should fix regardless (by removing the check for data already being present...if the user invokes set we should follow that imho especially since the value is not provided by the submission and it's just undefined).

However I think we should have a better API for this by providing the ability to use a value directly in the template taking inspiration by the hidden and submit types.

Describe the proposed solution

Forms fields should accept a second argument, which is the value of the form:

<script>
	import { get_things, add_thing, edit_thing } from './things.remote.ts';
	import { untrack } from "svelte";

	let things = $derived(await get_things());
</script>

{#each things as thing}
	{@const edit_form = edit_thing.for(thing.id)}
	<form {...edit_form}>
		<input {...edit_form.fields.name.as("text", thing.name) />
		<input {...edit_form.fields.age.as("number", thing.age) />
		<button {...edit_form.fields.id.as("submit", thing.id)>Save</button>
	</form>
{/each}

this could also be solved by #14815 if we allow the form factory to receive the initial values.

Alternatives considered

Keep the hack version but fix the submit bug.

Importance

would make my life easier

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    formsStuff relating to forms and form actions
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions