admin管理员组

文章数量:1122846

I'm working with Svelte 5 and using Superforms for form handling. My goal is to bind a CalendarDate object from a custom DatePicker component to a Superforms field and then convert that CalendarDate to a string before submitting the form. However, when I submit the form, the value is not getting correctly passed and saved. It always goes to the 'NOTHING' default value, but al the logs show a if the reactivity and the changes works as expected.

Here's the important bit of the component:

<script lang="ts">
    import type { DateValue } from '@internationalized/date';
    import { DatePicker } from 'bits-ui';

    interface Props {
        label?: string,
        selectedValue?: DateValue,
        width?: string,
        padding?: string,
        borderRadius? : string
    }

    let { label, selectedValue = $bindable(), width = '100%', borderRadius = '7px', padding = '0.75rem' }: Props = $props()

</script>

<div class="input-wrapper flex flex-col" style="width: {width};">
<DatePicker.Root weekdayFormat="short" fixedWeeks={true} bind:value={selectedValue}>
        <DatePicker.Label class="pb-2 pl-1 text-sm-typo">{label}</DatePicker.Label>
        <DatePicker.Input style="border-radius: {borderRadius}; width: {width} padding: {padding}">
            {#snippet children({ segments })}
                {#each segments as { part, value }}
                        {#if part === 'literal'}
                            <DatePicker.Segment {part} >
                                {value}
                            </DatePicker.Segment>
                        {:else}
                            <DatePicker.Segment {part} >
                                {value}
                            </DatePicker.Segment>
                        {/if}
                {/each}
                <DatePicker.Trigger>

Then the +page.svelte:

<script lang="ts">
    import DatePicker from "$src/components/elements/dates/DatePicker.svelte";
    import { superForm } from "sveltekit-superforms/client";
    import ShinyCta from "$src/components/elements/buttons/ShinyCTA.svelte";
    import { CalendarDate, parseDate } from "@internationalized/date";

    let { data } = $props();

    const { form, errors, enhance, message } = superForm(data.form, {
        validators: 'clear',
    });

    let defaultValue = new CalendarDate(1997, 1, 9)

    let calendarDate: CalendarDate | undefined = $state($form.testDate ? parseDate($form.testDate) : defaultValue) 

    $effect(()=> {
        if (calendarDate) {
            $form.testDate = calendarDate.toString(); // Convert CalendarDate to string
        
    }})
</script>

<form action="" method="post" use:enhance>
    <DatePicker bind:selectedValue={calendarDate}/>
    <input type="hidden" name="testDate" value={$form.testDate || 'NOTHING'} />
    <ShinyCta type='submit' btnWidth='100%'>Submit</ShinyCta>
</form>

And the +page.server.ts:

import { superValidate } from "sveltekit-superforms/server";
import type { PageServerLoad } from "./$types";
import { zod } from "sveltekit-superforms/adapters";
import { testSchema } from "./testValidation";
import type { Actions } from "@sveltejs/kit";

export const load: PageServerLoad = async () => {
    const form = await superValidate(zod(testSchema))
    return { form }
};

export const actions: Actions = {
    default: async ({ request }) => {
        const form = await superValidate(request, zod(testSchema));

        if (!form.valid) {
            console.log("Form invalid:", form.errors);
            return { form };
        }

        // Extract the string date for database storage
        console.log('FORM DATA', form.data)

        // Save to the database (e.g., PostgreSQL)
        // await db.insert({ date: testDate });

        return { form, success: true };
    }
};

The form validation should be as a String? Or can I make it expect a CalendarDate object (eventhough I'd like to store the date in my db as a ISO string)

I'm working with Svelte 5 and using Superforms for form handling. My goal is to bind a CalendarDate object from a custom DatePicker component to a Superforms field and then convert that CalendarDate to a string before submitting the form. However, when I submit the form, the value is not getting correctly passed and saved. It always goes to the 'NOTHING' default value, but al the logs show a if the reactivity and the changes works as expected.

Here's the important bit of the component:

<script lang="ts">
    import type { DateValue } from '@internationalized/date';
    import { DatePicker } from 'bits-ui';

    interface Props {
        label?: string,
        selectedValue?: DateValue,
        width?: string,
        padding?: string,
        borderRadius? : string
    }

    let { label, selectedValue = $bindable(), width = '100%', borderRadius = '7px', padding = '0.75rem' }: Props = $props()

</script>

<div class="input-wrapper flex flex-col" style="width: {width};">
<DatePicker.Root weekdayFormat="short" fixedWeeks={true} bind:value={selectedValue}>
        <DatePicker.Label class="pb-2 pl-1 text-sm-typo">{label}</DatePicker.Label>
        <DatePicker.Input style="border-radius: {borderRadius}; width: {width} padding: {padding}">
            {#snippet children({ segments })}
                {#each segments as { part, value }}
                        {#if part === 'literal'}
                            <DatePicker.Segment {part} >
                                {value}
                            </DatePicker.Segment>
                        {:else}
                            <DatePicker.Segment {part} >
                                {value}
                            </DatePicker.Segment>
                        {/if}
                {/each}
                <DatePicker.Trigger>

Then the +page.svelte:

<script lang="ts">
    import DatePicker from "$src/components/elements/dates/DatePicker.svelte";
    import { superForm } from "sveltekit-superforms/client";
    import ShinyCta from "$src/components/elements/buttons/ShinyCTA.svelte";
    import { CalendarDate, parseDate } from "@internationalized/date";

    let { data } = $props();

    const { form, errors, enhance, message } = superForm(data.form, {
        validators: 'clear',
    });

    let defaultValue = new CalendarDate(1997, 1, 9)

    let calendarDate: CalendarDate | undefined = $state($form.testDate ? parseDate($form.testDate) : defaultValue) 

    $effect(()=> {
        if (calendarDate) {
            $form.testDate = calendarDate.toString(); // Convert CalendarDate to string
        
    }})
</script>

<form action="" method="post" use:enhance>
    <DatePicker bind:selectedValue={calendarDate}/>
    <input type="hidden" name="testDate" value={$form.testDate || 'NOTHING'} />
    <ShinyCta type='submit' btnWidth='100%'>Submit</ShinyCta>
</form>

And the +page.server.ts:

import { superValidate } from "sveltekit-superforms/server";
import type { PageServerLoad } from "./$types";
import { zod } from "sveltekit-superforms/adapters";
import { testSchema } from "./testValidation";
import type { Actions } from "@sveltejs/kit";

export const load: PageServerLoad = async () => {
    const form = await superValidate(zod(testSchema))
    return { form }
};

export const actions: Actions = {
    default: async ({ request }) => {
        const form = await superValidate(request, zod(testSchema));

        if (!form.valid) {
            console.log("Form invalid:", form.errors);
            return { form };
        }

        // Extract the string date for database storage
        console.log('FORM DATA', form.data)

        // Save to the database (e.g., PostgreSQL)
        // await db.insert({ date: testDate });

        return { form, success: true };
    }
};

The form validation should be as a String? Or can I make it expect a CalendarDate object (eventhough I'd like to store the date in my db as a ISO string)

Share Improve this question asked Nov 22, 2024 at 10:43 ErosiqueErosique 11 bronze badge 3
  • I tried this with a regular <input type=date> for the date selection and the value is sent just fine. (The input already uses a string, so I converted it to a full ISO 8601 date/time to differentiate.) – brunnerh Commented Nov 22, 2024 at 16:40
  • If you observe the 'NOTHING' value on the page after the submit: That would be expected because you don't load anything in your load function. – brunnerh Commented Nov 22, 2024 at 17:00
  • Thank you so much for heping! Thoough the problem came from DatePicker Component from BitsUi using a DateValue type for the value (based upon the CalendarDate type of @internationalized package). I actually managed to fix it and found that the mistake was way simpler than the convoluted way I was trying. I respond to my own ticket so you can check if you want: – Erosique Commented Nov 22, 2024 at 22:39
Add a comment  | 

1 Answer 1

Reset to default 0

After tying I just simplified my approach and created a Hidden input to store the DateValue | CalendarDate value type of the Date Picker BitsUi component after parsing it into a string with toString and sync it with the Component Value with the 'onValueChange' prop. Like this:

DatePicker.svelte:

    <script lang="ts">
    import { parseDate, type CalendarDate, type DateValue } from '@internationalized/date';
    import { DatePicker } from 'bits-ui';

    interface Props {
        label?: string,
        width?: string,
        padding?: string,
        borderRadius?: string,
        testDate: string 
    }

    
    let { testDate = $bindable(), label, width = '100%', borderRadius = '7px', padding = '0.75rem' }: Props = $props()
    
    let calendarDate: DateValue = $state(parseDate(testDate))

</script>

<div class="input-wrapper flex flex-col" style="width: {width};">
    <input type="hidden" name="testDate" bind:value={testDate}>
<DatePicker.Root weekdayFormat="short" fixedWeeks={true} bind:value={calendarDate}  onValueChange={(value) => testDate = value.toString()}>

And then in the +page.svelte:

  <script lang="ts">
    import DatePicker from "$src/components/elements/dates/DatePicker.svelte";
    import { superForm } from "sveltekit-superforms/client";
    import ShinyCta from "$src/components/elements/buttons/ShinyCTA.svelte";
    import { today } from "@internationalized/date";

    let { data } = $props();
    const { form, enhance } = superForm(data.form);

    if(!$form.testDate) {
        $form.testDate = today('UTC').toString()
    }
</script>

<form action="" method="post" use:enhance>
    <DatePicker bind:testDate={$form.testDate}/>
    <ShinyCta type='submit' btnWidth='100%'>Submit</ShinyCta>
</form>

IMPORTANT!: Just make sure to put the same name attribute than the one expected by the form!! That was giving me errors when not matching!

本文标签: sveltekitSvelte 5 and Superforms Error Submiting Value of a DatePicker ComponentStack Overflow