realworld icon indicating copy to clipboard operation
realworld copied to clipboard

Form actions from a component.

Open juandld opened this issue 1 year ago • 1 comments

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.

juandld avatar Sep 05 '24 05:09 juandld

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: '[email protected]', password: 'a' }

I hope this helps!

wackbyte avatar Oct 22 '24 01:10 wackbyte