フォームアクション
サーバーサイドでフォームを処理する
フォームアクションとは?
日常での例え:窓口での申請処理
申請書(フォーム)に記入して窓口に提出
職員(サーバー)が内容をチェック・処理
結果を返す(成功/エラー)
なぜフォームアクションが便利?
従来の方法:fetch() で自分でAPIを呼び出す → コードが複雑に
フォームアクション:HTMLの <form> を使うだけ!
- JavaScriptが無効でも動作する(アクセシビリティ)
- バリデーションエラーと入力値の復元が簡単
- use:enhance でJSによる機能強化も可能
基本的なフォームアクション
アクションの定義
+page.server.tsにactionsを定義
typescript
// src/routes/login/+page.server.ts
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
// バリデーション
if (!email || !password) {
return { success: false, error: '入力してください' };
}
// ログイン処理...
return { success: true };
}
};プレビュー
// form の method="POST" で
// actions.default が呼ばれる
フォームの実装
method=POSTで送信
svelte
<!-- src/routes/login/+page.svelte -->
<script>
let { form } = $props();
</script>
<form method="POST">
<input name="email" type="email" required>
<input name="password" type="password" required>
<button type="submit">ログイン</button>
</form>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
{#if form?.success}
<p class="success">ログイン成功!</p>
{/if}プレビュー
名前付きアクション
複数のアクション
action属性で指定
typescript
// src/routes/posts/+page.server.ts
export const actions = {
create: async ({ request }) => {
const data = await request.formData();
const title = data.get('title');
await createPost({ title });
return { success: true };
},
delete: async ({ request }) => {
const data = await request.formData();
const id = data.get('id');
await deletePost(id);
return { deleted: true };
}
};プレビュー
actions.create
actions.delete
名前付きアクションの呼び出し
action=?/アクション名
svelte
<!-- 投稿作成フォーム -->
<form method="POST" action="?/create">
<input name="title" required>
<button>投稿</button>
</form>
<!-- 削除フォーム -->
<form method="POST" action="?/delete">
<input type="hidden" name="id" value={post.id}>
<button>削除</button>
</form>プレビュー
バリデーション
fail関数でエラーを返す
入力値を保持してエラー表示
typescript
// src/routes/register/+page.server.ts
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get('email')?.toString();
const password = data.get('password')?.toString();
// バリデーション
if (!email?.includes('@')) {
return fail(400, {
email, // 入力値を返す
error: 'メールアドレスが無効です'
});
}
if (password && password.length < 8) {
return fail(400, {
email,
error: 'パスワードは8文字以上'
});
}
// 登録処理...
return { success: true };
}
};プレビュー
fail(400, { error: '...' })
// ステータス400でエラーを返す
// 入力値も一緒に返せる
エラー表示とフォームの復元
form propsでアクセス
svelte
<script>
let { form } = $props();
</script>
<form method="POST">
<label>
メールアドレス
<input
name="email"
type="email"
value={form?.email ?? ''}
>
</label>
<label>
パスワード
<input name="password" type="password">
</label>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<button>登録</button>
</form>プレビュー
プログレッシブエンハンスメント
use:enhance
JavaScriptでフォームを強化
svelte
<script>
import { enhance } from '$app/forms';
let loading = $state(false);
</script>
<form
method="POST"
use:enhance={() => {
loading = true;
return async ({ result, update }) => {
loading = false;
if (result.type === 'success') {
// 成功時の処理
}
await update(); // デフォルトの更新処理
};
}}
>
<button disabled={loading}>
{loading ? '送信中...' : '送信'}
</button>
</form>プレビュー
// use:enhance で
// - ページ遷移なしで送信
// - ローディング状態の管理
// - 成功/失敗時のカスタム処理
アクション後のリダイレクト
成功後にリダイレクト
redirect関数を使用
typescript
// src/routes/login/+page.server.ts
import { redirect, fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request, cookies }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
const user = await login(email, password);
if (!user) {
return fail(401, {
email,
error: 'メールアドレスまたはパスワードが違います'
});
}
// セッションを設定
cookies.set('session', user.sessionId, { path: '/' });
// ダッシュボードへリダイレクト
redirect(303, '/dashboard');
}
};プレビュー
// 成功 → redirect()
// 失敗 → fail() でエラーを返す