ciscoheat / sveltekit-superforms

Making SvelteKit forms a pleasure to use!
https://superforms.rocks
MIT License
2.23k stars 66 forks source link

Binding with forms does not seem to work #33

Closed stefandevo closed 1 year ago

stefandevo commented 1 year ago

Not sure if this is Superforms specific.

I have a component inside of a form:

                    <PhoneNumber
                        name="phoneNumber"
                        bind:phone={$form.phoneNumber}
                        label="Your mobile number"
                        errors={$errors.phoneNumber}
                        {...$constraints.phoneNumber}
                    />

I have the debugging form available. I see that $form.phoneNumber is updated with a specific value, however, on submit of the form, the value in form.data.phoneNumber on page.server does not reflect the value of $form.phoneNumber

Example:

CleanShot 2023-03-15 at 12 45 53@2x

as you can see the phone number has a value starting with 31.

However, when I submit, I get 654654654 as the value of phoneNumber in the page.server form.data.phoneNumber

Any ideas? In the component itself I change the phone value based upon the dropdown and the entered number. So I compose two values in the phone variable. But as you can see, the form values are correct client side.

ciscoheat commented 1 year ago

If you set options.dataType = 'json', the actual content of $form will be posted. But if you haven't set it, it will, according to the html standard, post the fields in the <form> element. So I suspect that you compose the values to $form, but they are not reflected in the form fields.

stefandevo commented 1 year ago

Wow! Hero!

stefandevo commented 1 year ago

Sorry to come back... I am now facing a small other issue :-)

I am calculation a formid value. I am now using:

    const { form, errors, constraints, enhance } = superForm(data.form, {
        dataType: 'json',
        onSubmit(input) {
            $form.formid = getFormId();
        }
    });

this works fine. But because I want to use this everywhere, I thought I could make a generic function:

import { superForm } from 'sveltekit-superforms/client';
import type { AnyZodObject } from 'zod';

export function hSuperForm<T extends AnyZodObject>(
  ...params: Parameters<typeof superForm<T>>
) {
  return superForm(params[0], {
    // Your defaults here
    dataType: 'json',
    onSubmit(input) {
        $form.formid = getFormId();
    },
    ...params[1]
  });
}

of course this does not work as $form.formid is not known.

Any suggestion to be able to add a generic way of adding a property formid here?

ciscoheat commented 1 year ago

In that case, maybe you can extend from the schema that contains the formid field, that all other schemas should derive from?

hSuperForm<T extends typeof formIdSchema>

(Just a sidenote that you already know probably: dataType = 'json' makes javascript a requirement.)

stefandevo commented 1 year ago

Okay, that's a good suggestion; but still, how would I set the value from with the hSuperForm function? Starting from the onSubmit(input) how can I set this value? I don't see any way to do this?

ciscoheat commented 1 year ago

Ah, now I see that it's onSubmit. If you just want to post an extra field, add it to the FormData of the submit event.

onSubmit({data}) {
  data.set('formid', getFormId())
}
ciscoheat commented 1 year ago

Check this out for event signature: https://kit.svelte.dev/docs/types#public-types-submitfunction

stefandevo commented 1 year ago

Well, that's basically what does not work. As from I change the dataType = 'json' I cannot set any extra fields, or at least it is not added to the object itself. So I hoped that this would allow me to add the formid as the property of the formIdSchema schema.

Now using

        const data = await request.formData();
        const formid = data.get('formid') as string;

        const form = await superValidate(data, schema);
        if (!form.valid) {
            return fail(400, { form });
        }
        form.data.formid = formid;

but now I have to repeat code everytime here :-(

Other option is

    const { form, errors, constraints, enhance } = hSuperForm(data.form, {
        onSubmit(input) {
            $form.formid = getFormId();
        }
    });

in every page.

So just looking for the most optimal way of doing this.

ciscoheat commented 1 year ago

Yes, it cannot be added to the object, since its type doesn't have a formid field. :) I'd go for the first way you're doing it, but factorize it to a function.

stefandevo commented 1 year ago

I have now create an wrapper around the superValidate

export async function hSuperValidate<T extends AnyZodObject>(
    req: Request,
    schema: T,
    options?: SuperValidateOptions<T>
): Promise<Validation<T>> {
    const data = await req.formData();
    const formid = data.get('formid') as string;

    const form = await superValidate(data, schema, options);
    //@ts-ignore
    form.data.formid = formid;
    return form;
}

the only dirty thing is the part where I set the formid... of course it's giving me a ts error, but I just ignore it.

stefandevo commented 1 year ago

I wish I could use a Type instead of AnyZodObject that contains the formid

I am using

export const hFormObject = z.object({
    formid: z.string()
});

and then

const schema = hFormObject.extend({
    phoneNumber: z.string().min(9),
    remember: z.boolean().default(false)
});

as a base for my schema's, but I have no clue how to make this a type I can use.

ciscoheat commented 1 year ago

Doesn't this work?

export async function hSuperValidate<T extends typeof schema> // (or hFormObject)
stefandevo commented 1 year ago

🤦

yes!

ciscoheat commented 1 year ago

Great, I'll close this then. :)