家計簿アプリを作る #6:収支の編集を実装する

家計簿アプリ作成シリーズの第6回です。今回は登録済みの収支データを編集できるようにします。

ファイル構成

src/routes/
  transactions/
    [id]/
      edit/
        +page.server.ts  # 新規作成:データ取得 + 更新アクション
        +page.svelte     # 新規作成:編集フォーム
  +page.svelte           # 変更:編集ボタンを追加

+page.svelte(ルート):編集ボタンを追加する

一覧の各行に編集ボタンを追加します。編集は別ページへの遷移なので、Form Action ではなく <a> タグで実装します。

<td>
  <a href={`/transactions/${transaction.id}/edit`}>編集</a>
</td>

削除はデータ変更を伴うので Form Action が適切ですが、別ページへの遷移は <a> タグで十分です。

+page.server.ts:load と update アクションを実装する

import { fail, redirect } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
import { createDb } from '$lib/server/db';
import { transactions } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';

export const load: PageServerLoad = async ({ platform, params }) => {
  const db = createDb(platform!.env.DB);

  const transaction = await db.select().from(transactions)
    .where(eq(transactions.id, params.id))
    .get();

  if (!transaction) {
    redirect(303, '/');
  }

  return { transaction };
};

export const actions: Actions = {
  update: async ({ request, platform, params }) => {
    const formData = await request.formData();
    const amount = formData.get('amount');
    const type = formData.get('type');
    const category = formData.get('category');
    const memo = formData.get('memo') || '';
    const date = formData.get('date');

    if (!amount || !type || !category || !date) {
      return fail(400, { error: 'All fields except memo are required.' });
    }

    const db = createDb(platform!.env.DB);

    await db.update(transactions)
      .set({
        amount: Number(amount),
        type: String(type),
        category: String(category),
        memo: String(memo),
        date: String(date)
      })
      .where(eq(transactions.id, params.id));

    redirect(303, '/');
  }
};

ポイント:

  • loadparams.id を使って該当データを1件取得する。見つからない場合は / にリダイレクト
  • update アクションでも params.id を使うため、hidden input で id をフォームに持たせる必要はない
  • Drizzle の更新は db.update().set({...}).where(eq(...)) の構成

+page.svelte:編集フォームを作る

<script lang="ts">
  import { enhance } from "$app/forms";

  let { data, form } = $props();
</script>

<form method="POST" action="?/update" use:enhance>
  <div>
    <label for="date">日付:</label>
    <input type="date" id="date" name="date" value={data.transaction.date} required />
  </div>
  <div>
    <label for="type">種類:</label>
    <select id="type" name="type" required>
      <option value="income" selected={data.transaction.type === "income"}>収入</option>
      <option value="expense" selected={data.transaction.type === "expense"}>支出</option>
    </select>
  </div>
  <div>
    <label for="category">カテゴリ:</label>
    <input type="text" id="category" name="category" value={data.transaction.category} required />
  </div>
  <div>
    <label for="memo">メモ:</label>
    <input type="text" id="memo" name="memo" value={data.transaction.memo} />
  </div>
  <div>
    <label for="amount">金額:</label>
    <input type="number" id="amount" name="amount" value={data.transaction.amount} required />
  </div>
  <button type="submit">更新</button>
</form>

{#if form?.error}
  <p style="color: red">{form.error}</p>
{/if}

ポイント:

  • 各 input の valuedata.transaction.xxx を渡すことで現在の値が初期表示される
  • <select>selected={data.transaction.type === "income"} のように条件で選択状態を制御する

まとめ

ポイント 内容
編集ボタン 別ページへの遷移なので <a> タグで実装
データ取得 db.select().where(eq(...)).get() で1件取得
id の受け渡し params.id で取得するため hidden input は不要
更新 db.update().set({...}).where(eq(...))
初期値の表示 input の valuedata.transaction.xxx を渡す

次回は認証を実装します。

← トップページに戻る