こんにちは。よっしーです(^^)
今日は、SvelteKitでのフォームアクションについて解説しています。
背景
SvelteKitでのフォームアクションについて調査する機会がありましたので、その時の内容を備忘として記事に残しました。
SvelteKitでのフォームアクションについて
+page.server.js
ファイルでは「アクション」をエクスポートできます。これにより、<form>
要素を使用してサーバーにデータをPOST
することが可能になります。<form>
を使用する際、クライアントサイドのJavaScriptは任意ですが、JavaScriptを使って簡単にフォームの操作を段階的に強化し、最高のユーザー体験を提供することができます。
アクションの構造
各アクションはRequestEvent
オブジェクトを受け取り、これによりrequest.formData()
を使用してデータを読み取ることができます。リクエストの処理(例えば、クッキーを設定してユーザーをログインさせるなど)の後、アクションはデータを返すことができます。このデータは、次の更新まで、対応するページのform
プロパティを通じて、そしてアプリ全体で$page.form
を通じて利用可能になります。
// src/routes/login/+page.server.js
/** @type {import('./$types').PageServerLoad} */
export async function load({ cookies }) {
const user = await db.getUserFromSession(cookies.get('sessionid'));
return { user };
}
/** @type {import('./$types').Actions} */
export const actions = {
login: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const user = await db.getUser(email);
cookies.set('sessionid', await db.createSession(user), { path: '/' });
return { success: true };
},
register: async (event) => {
// TODO register the user
}
};
// src/routes/login/+page.svelte
<script>
/** @type {import('./$types').PageData} */
export let data;
/** @type {import('./$types').ActionData} */
export let form;
</script>
{#if form?.success}
<!-- this message is ephemeral; it exists because the page was rendered in
response to a form submission. it will vanish if the user reloads -->
<p>Successfully logged in! Welcome back, {data.user.name}</p>
{/if}
Validation errors
無効なデータのためにリクエストを処理できなかった場合、バリデーションエラーを、以前に送信されたフォームの値とともにユーザーに返すことができます。これにより、ユーザーは再試行することができます。fail
関数を使用すると、HTTPステータスコード(バリデーションエラーの場合は通常400または422)をデータと一緒に返すことができます。このステータスコードは$page.status
を通じて、データはform
を通じて利用可能になります。
// src/routes/login/+page.server.js
import { fail } from '@sveltejs/kit';
/** @type {import('./$types').Actions} */
export const actions = {
login: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
if (!email) {
return fail(400, { email, missing: true });
}
const user = await db.getUser(email);
if (!user || user.password !== hash(password)) {
return fail(400, { email, incorrect: true });
}
cookies.set('sessionid', await db.createSession(user), { path: '/' });
return { success: true };
},
register: async (event) => {
// TODO register the user
}
};
予防措置として、メールはページに返されるだけであり、パスワードは返されないことに注意してください。
src/routes/login/+page.svelte
<form method="POST" action="?/login">
{#if form?.missing}<p class="error">The email field is required</p>{/if}
{#if form?.incorrect}<p class="error">Invalid credentials!</p>{/if}
<label>
Email
<input name="email" type="email" value={form?.email ?? ''}>
</label>
<label>
Password
<input name="password" type="password">
</label>
<button>Log in</button>
<button formaction="?/register">Register</button>
</form>
返されるデータはJSONとしてシリアライズ可能でなければなりません。それ以外の構造は完全に自由です。例えば、ページに複数のフォームがある場合、返されるform
データがどの<form>
を参照しているかを、id
プロパティなどを使って区別することができます。
リダイレクト
リダイレクト (およびエラー) はロード時とまったく同じように機能します。
src/routes/login/+page.server.js
import { fail, redirect } from '@sveltejs/kit';
/** @type {import('./$types').Actions} */
export const actions = {
login: async ({ cookies, request, url }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const user = await db.getUser(email);
if (!user) {
return fail(400, { email, missing: true });
}
if (user.password !== hash(password)) {
return fail(400, { email, incorrect: true });
}
cookies.set('sessionid', await db.createSession(user), { path: '/' });
if (url.searchParams.has('redirectTo')) {
redirect(303, url.searchParams.get('redirectTo'));
}
return { success: true };
},
register: async (event) => {
// TODO register the user
}
};
おわりに
今日は、 SvelteKitでのフォームアクションについて解説しました。
何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)
コメント