家計簿アプリを作る #12:月別フィルタの実装

家計簿アプリ作成シリーズの第12回です。今回は収支一覧に月別フィルタを実装します。

実装方針

選択した年月を URL のクエリパラメータ(?month=2026-06)として渡し、load 関数で受け取って絞り込みます。

Step 1:年月の選択肢を生成する

全件取得した後、日付から年月パターンを抽出して降順にソートします。

const allTransactions = await db.select().from(transactions);

const uniqueMonths = Array.from(
  new Set(
    allTransactions.map((t) => {
      const date = new Date(t.date);
      return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
    }).sort((a, b) => (a > b ? -1 : 1))
  )
);

Step 2:クエリパラメータで年月を受け取り絞り込む

load 関数の引数に url を追加して searchParams で受け取ります。

export const load: PageServerLoad = async ({ locals, platform, url }) => {
  const selectedMonth = url.searchParams.get('month');

Drizzle の like() で該当月のデータを絞り込みます。

import { like } from 'drizzle-orm';

const selectedTransactions = selectedMonth
  ? await db
      .select()
      .from(transactions)
      .where(like(transactions.date, `${selectedMonth}%`))
  : allTransactions;

ハマりポイント:selectedMonthnull のとき

初回アクセス時(クエリパラメータなし)は selectedMonthnull になります。このまま like() に渡すと date LIKE 'null%' になり0件が返ってしまいます。

三項演算子で null のときは全件(allTransactions)をそのまま使うことで対処しました。

データが0件の場合も考慮すると、A案(最新月を自動選択)では uniqueMonths[0]undefined になり同じ問題が起きるため、B案(null のときは全件表示)が安全です。

Step 3:+page.svelte<select> を実装する

goto でクエリパラメータを変えてページ遷移します。

<script lang="ts">
  import { goto } from '$app/navigation';
  let { data, form } = $props();
</script>

<select
  onchange={(e) => goto(`/?month=${e.currentTarget.value}`)}
>
  {#each data.months as month}
    <option value={month} selected={month === data.selectedMonth}>
      {month}
    </option>
  {/each}
</select>

Step 4:削除後もフィルタを維持する

削除 action の redirect でクエリパラメータをリセットしないよう対処します。

delete: async ({ request, platform, url }) => {
  // ...削除処理...

  const selectedMonth = url.searchParams.get('month');
  redirect(303, selectedMonth ? `/?month=${selectedMonth}` : '/');
}

selectedMonthnull(月未選択)のときは / にリダイレクトします。

まとめ

ポイント 内容
クエリパラメータの受け取り load 関数の引数に url を追加して url.searchParams.get('month') で取得
絞り込み like(transactions.date, \${selectedMonth}%`)`
null チェック selectedMonthnull のときは全件表示にフォールバック
削除後のリダイレクト url.searchParams.get('month') を action でも取得してクエリパラメータを維持

次回は収支の登録・編集フォームに月別フィルタの選択状態を引き継ぐ機能を実装します。

← トップページに戻る