Closed kigathi-chege closed 7 months ago
Setting default values for fields will make them optional in Superforms, so this is probably not the best approach, and I'm not sure you want it anyway, if you're editing existing records. Doesn't it work to populate it as usual when you call superValidate
? https://superforms.rocks/get-started#populate-form-from-database
I was thinking of posting a similar question here as I have a similar use-case I'm not sure I'm handling correctly.
I have a form to edit the metadata of an item. However, the form only appears when the user (from a list of items) presses the "Edit" button on one of them.
So the default values should be being set then. Client-side.
Does this make sense then? When submitting, none of the data is inclnuded in the POST body. I've removed the name
attributes from the form field as I see they're not required in dataType: 'json'
mode. But there doesn't seem to be a replacement JSON Body of the data on the POST request when submitting then :thinking:
import { zodClient } from "sveltekit-superforms/adapters"
import SuperDebug, { defaults, superForm } from "sveltekit-superforms"
// `ui` is a global $state "store" that gets filled when an item from the list is selected to be edited.
const defaultData = {
id: ui.metadataSidebarData.bookmark?.id,
url: ui.metadataSidebarData.bookmark?.url,
title: ui.metadataSidebarData.bookmark?.title,
description: ui.metadataSidebarData.bookmark?.desc,
category: ui.metadataSidebarData.bookmark?.category,
tags: ui.metadataSidebarData.bookmark?.tags,
}
const superformInstance = superForm(defaults(defaultData, zodClient($page.data.metadataForm)), {
resetForm: false,
dataType: "json",
validators: zodClient(metadataSchema),
onUpdated: ({ form }) => {
if (form.message?.text) {
toast.success(form.message.text)
}
},
onError: ({ result }) => {
if (result.type === "error") {
toast.error(result.error.message)
}
},
})
Where $page.data.metadataForm
is the this return value from +page.server.ts
:
return {
metadataForm: await superValidate(zod(metadataSchema))
}
And this is the schema:
import { z } from "zod"
export const metadataSchema = z.object({
id: z.string().max(500).cuid(),
title: z.string({ required_error: "A title is required" }).min(2).max(100),
url: z.string({ required_error: "A URL is required" }).url().max(100),
description: z.string().max(500).optional(),
image: z.string().url().optional(),
category: z.object({
id: z.string().cuid(),
name: z.string(),
description: z.string().optional(),
userId: z.string().min(2).max(50),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
}),
tags: z.object({
id: z.string().cuid(),
name: z.string(),
userId: z.string().min(2).max(50),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
}),
})
export type FormSchema = typeof formSchema
There seems to be an additional problem, where as soon as I begin editing/typing into one of the input fields for one of the simple string fields, for example like title
, I begin getting svelte too-many-updates errors in the console :/
Svelte component looks like this:
<input
type="text"
id="title"
readonly={!isEditMode}
bind:value={$form.data.title}
aria-invalid={$errors.title ? "true" : undefined}
{...$constraints.title}
class="bunch of tailwind classes.."
/>
@ndom91 It's hard to say without more context, so please make an MRE here on Stackblitz, and then post in one of the support channels on Discord, then I can take a closer look!
Sounds good, thanks!
Okay so I got it working, but there was one thing which deviates from the docs that I either misunderstood, or might need to be updated. After fixing this all the other issues went away as well :joy:
Using client-side defaults()
as the first arguemnt to superForm
results in the returned form
value (i.e. const { form } = superForm()
) to be the entire form, incl. id
, errors
fields, etc. which messes up the rest of the behaviour. This returned $form
store should be an object with only the form data, right?
I got it to work by setting the defaults().data
as the first argument to superForm()
, not the entire return value of defaults()
:
const superformInstance = superForm(
+ defaults(defaultData, zodClient($page.data.metadataForm)).data,
- defaults(defaultData, zodClient($page.data.metadataForm)),
{
resetForm: false,
dataType: "json",
validators: zodClient(metadataSchema),
}
)
defaults returns a SuperValidated
type, which is used to instantiate superForm. If you do it as you do now, you'll get no constraints and could have problem with error mapping, as they are a part of the SuperValidated
type.
Hmm okay, so what would you recommend?
Doing this
const superformInstance = superForm(
defaults(defaultData, zodClient($page.data.metadataForm)),
{
resetForm: false,
dataType: "json",
validators: zodClient(metadataSchema),
}
)
Results in:
const { form } = superForm(..)
console.log($form)
{
id: '',
valid: true,
posted: true,
errors: {},
data: {
id: 'clsp4u0050008ave480bxp3ly',
title: 'Engadget',
url: 'https://engadget.com',
description: 'Find the latest technology news....',
image: 'https://s.yimg.c..../b.jpg',
category: {
id: 'clsordi7y0005ave4risddzf5',
name: 'Category2',
description: 'Cat2',
userId: 'cls57rev90000iw28cg9fl2nr',
createdAt: 2024-02-16T14:41:59.422Z,
updatedAt: 2024-02-16T14:41:59.422Z
},
tags: []
}
}
So then I have to go through all my form fields and set <Input bind:value={$form.data.title} />
instead of bind:value={$form.title}
. Then whenever I update the data with jsonData
in onSubmit
upon return the $form
is just the "normal" form data object containing only the data key/values (i.e. data
object from above) :thinking:
I feel like I'm misunderstanding something critical here maybe haha. Will try to put together a simple stackblitz repro when I have a minute :+1:
Interestingly this validation onBlur
and error mapping / rendering does seem to work in that case of using .data
There is something strange about how you use zodClient
. Here you should send a Zod schema to it, but it looks like you're passing a SuperValidated
object:
zodClient($page.data.metadataForm)
There should be no Zod schemas in $page.data, as they cannot be serialized, so something is wrong with how you call it. It looks more like you should just call superForm
directly.
Ah good catch. But fixing that still results in my superForm
returned form
to basically also be a SuperValidated
type, instead of SuperFormData
:thinking:
Compare your code with the getting started tutorial on the website, and see what differs. If you can't figure it out, make an MRE.
I've been going throguh the "getting started" and comparing stuff all day :sweat_smile:
Anyway, here's a super simple repro: https://stackblitz.com/edit/sveltejs-kit-template-default-z8bhfv?file=src%2Froutes%2F%2Bpage.svelte
It's having the same issue where superForms.form
is an instance of SuperValidated
(instead of SuperFormData
) and my defaultData
isn't appearing in the bound input fields.
I couldn't test the form submission stuff here though beacuse as soon as I submit the data, which does get included in the __superform_json
formData field, it crashes with an odd error i've never seen before, Error:
linemust be greater than 0 (lines start at line 1)
. Seems like it might not be related to superforms though
Long story short, defaults
failed the check for a SuperValidated
object. You should be able to use zod
instead of zodClient
to make it work, until it's fixed in the next release.
If it still doesn't work, try adding an id as an option.
Thanks for taking a closer look! Using the non-client version of the zod adapter worked for now :+1:
Should be fixed now with 2.6.1.
When editing an existing record instead of adding a new one, is it possible to set the default values to dynamic?
For example, I have:
My initial idea was to do this:
This however doesn't seem to work, and I am left wondering whether I am facing a sveltekit reactivity issue, or whether I am doing this the wrong way.
Either way, is there a way to dynamically set these default values?