家計簿アプリを作る #18:フォームのバリデーション強化
家計簿アプリ作成シリーズの第18回です。今回は収支の登録・編集フォームにバリデーションを実装します。
実装方針
SvelteKit の Form Actions では fail() で返したデータを +page.svelte の form プロパティで受け取れます。複数フィールドのエラーをまとめて返すには errors オブジェクトを使います。また values で入力値を返すことで、バリデーションエラー後も入力値がリセットされずに残ります。
バリデーション内容
- 金額:必須、かつ1以上
- 種類:必須
- カテゴリ:必須
- 日付:必須
Step 1:+page.server.ts にバリデーションを追加する
const errors: Record<string, string> = {};
// 金額は必須、かつ1以上
if (!amount) {
errors.amount = '金額を入力してください';
} else if (Number(amount) <= 0) {
errors.amount = '金額は1以上を入力してください';
}
// 種類は必須
if (!type) {
errors.type = '種類を入力してください';
}
// カテゴリは必須
if (!category) {
errors.category = 'カテゴリを入力してください';
}
// 日付は必須
if (!date) {
errors.date = '日付を入力してください';
}
if (Object.keys(errors).length > 0) {
return fail(400, {
errors,
values: { amount, type, category, memo, date }
});
}
各フィールドのエラーを errors オブジェクトに詰めていき、最後にまとめて fail() で返します。
ハマりポイント:金額チェックの順序
金額の必須チェックと1以上チェックを別々の if で書くと、後のチェックが前のエラーを上書きします。
// ❌ 空のとき「1以上」のエラーが「必須」で上書きされる
if (Number(amount) <= 0) {
errors.amount = '金額は1以上を入力してください';
}
if (!amount) {
errors.amount = '金額を入力してください';
}
// ✅ else if で排他的にチェックする
if (!amount) {
errors.amount = '金額を入力してください';
} else if (Number(amount) <= 0) {
errors.amount = '金額は1以上を入力してください';
}
Step 2:+page.svelte でエラーを表示する
各フィールドの直下にエラーメッセージを表示します。
<div>
<label for="amount">金額</label>
<input
type="number"
id="amount"
name="amount"
value={form?.values?.amount ?? ""}
required
/>
{#if form?.errors?.amount}
<p class="mt-1 text-sm text-fg-danger-strong">{form.errors.amount}</p>
{/if}
</div>
value={form?.values?.amount ?? ""} でバリデーションエラー後も入力値を保持します。
まとめ
| ポイント | 内容 |
|---|---|
| 複数エラーのまとめ返し | errors オブジェクトに詰めて fail() で一括返却 |
| 入力値の保持 | values を fail() に含めて form?.values で参照 |
| チェックの順序 | 必須チェックを先に、範囲チェックは else if で |
| エラー表示 | フィールドの直下に {#if form?.errors?.xxx} で表示 |