家計簿アプリを作る #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;
ハマりポイント:selectedMonth が null のとき
初回アクセス時(クエリパラメータなし)は selectedMonth が null になります。このまま 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}` : '/');
}
selectedMonth が null(月未選択)のときは / にリダイレクトします。
まとめ
| ポイント | 内容 |
|---|---|
| クエリパラメータの受け取り | load 関数の引数に url を追加して url.searchParams.get('month') で取得 |
| 絞り込み | like(transactions.date, \${selectedMonth}%`)` |
| null チェック | selectedMonth が null のときは全件表示にフォールバック |
| 削除後のリダイレクト | url.searchParams.get('month') を action でも取得してクエリパラメータを維持 |
次回は収支の登録・編集フォームに月別フィルタの選択状態を引き継ぐ機能を実装します。