sveltejs / realworld

SvelteKit implementation of the RealWorld app
https://realworld.svelte.dev
MIT License
2.26k stars 352 forks source link

Form actions from a component. #179

Open juandld opened 3 months ago

juandld commented 3 months ago

When trying to use a form action from a component and not from the +page file in the route, I will get a very strange behavior, it will work only if the form is empty.

I have a component that pops up to log in (Which could be a very common use case for any sort of form really):

<script lang="ts">
    import { enhance } from '$app/forms';
    import { popup } from '@skeletonlabs/skeleton';
    import type { PopupSettings } from '@skeletonlabs/skeleton';

    const popupClick: PopupSettings = {
        event: 'click',
        target: 'popupClick',
        placement: 'bottom'
    };

    let message = 'hello world';
    let email = '';
    let password = '';

</script>

<div class="card variant-ghost-surface p-4 flex justify-center items-center flex-col h-full">
    <h2 class="h2">Login</h2>

    <div class="m-2">
        <button class="btn variant-filled-secondary">Google</button>
        <button class="btn variant-filled-secondary">GitHub</button>
    </div>
    <h3>or</h3>
    <form class="grid grid-cols-1 gap-2" method="POST" action="/login" use:enhance>
        <label class="label">
            <span>Email</span>
            <input class="input" type="email" id="email"  />
        </label>
        <label class="label">
            <span>Password</span>
            <input class="input" type="password" id="password" />
        </label>
        <button class="btn variant-filled-primary" use:popup={popupClick}>Submit</button>
        <div class="card p-4 variant-filled-error" data-popup="popupClick">
            <p>{message}</p>
            <div class="arrow variant-filled-error" />
        </div>
    </form>
</div>

and this is the +page.server.ts file I have in the /login route

/** @type {import('./$types').Actions} */
export const actions = {
    default: async (event) => {
       //Will not even start if form has any value in the inputs
        console.log("started login");

        const data = await event.request.formData();
        console.log("data", data);

        const email = data.get('email');
        const password = data.get('password');
        return {
            email,
            password
        }        
    }
};

I wanted to use the actions feature to get better at Svelte but I just ended up wasting a bunch of time when I could have just assigned values and made the API request from the component itself.

wackbyte commented 1 month ago

The code uses the id attribute to name the form controls:

<input class="input" type="email" id="email"  />
<input class="input" type="password" id="password" />

However, forms require the name attribute to provide the names of the values in the submitted form data:

<input class="input" type="email" name="email"  />
<input class="input" type="password" name="password" />

Using id:

started login
data FormData {}

Using name:

started login
data FormData { email: 'a@a.a', password: 'a' }

I hope this helps!