家計簿アプリを作る #29:収支の月次推移グラフ
家計簿アプリ作成シリーズの第29回です。前回の選択月の収支グラフに加えて、複数月にわたる収支の月次推移グラフを追加します。
実装方針
allTransactions(全件取得済み)を月ごとに集計してmonthlyDataを作成+page.server.tsで集計してreturnで渡す+page.svelteで$derivedを使ってグラフデータに変換
+page.server.ts の実装
// 月次の収支の推移(昇順)
const monthlyData = [...uniqueMonths].reverse().map(month => {
const monthTx = allTransactions.filter(t => t.date.startsWith(month));
return {
month,
income: monthTx.filter(t => t.type === 'income').reduce((sum, t) => sum + t.amount, 0),
expense: monthTx.filter(t => t.type === 'expense').reduce((sum, t) => sum + t.amount, 0),
};
});
uniqueMonths は降順(新しい順)で作っているため、[...uniqueMonths].reverse() で昇順にしてからグラフ用に集計します。... でコピーしてから reverse() することで元の uniqueMonths(プルダウン用)には影響を与えません。
+page.svelte の実装
const monthlyData = $derived({
labels: data.monthlyData.map((d) => d.month),
datasets: [
{
label: '収入',
data: data.monthlyData.map((d) => d.income),
backgroundColor: 'rgba(34, 197, 94, 0.5)',
borderColor: 'rgb(34, 197, 94)',
borderWidth: 1,
},
{
label: '支出',
data: data.monthlyData.map((d) => d.expense),
backgroundColor: 'rgba(239, 68, 68, 0.5)',
borderColor: 'rgb(239, 68, 68)',
borderWidth: 1,
},
],
});
datasets に収入・支出の2つを追加することで、同じグラフに2本の棒が並びます。
グラフにタイトルを付ける
options.plugins.title でタイトルを表示できます。Title を Chart.register() に追加する必要があります。
import { Chart, BarElement, CategoryScale, LinearScale, Tooltip, Legend, Title } from "chart.js";
Chart.register(BarElement, CategoryScale, LinearScale, Tooltip, Legend, Title);
<Bar
data={monthlyData}
options={{
plugins: {
title: {
display: true,
text: '月次推移',
},
},
}}
/>
datasets の label を省略しない
datasets の各オブジェクトに label を設定しないと、グラフ上部の凡例に undefined と表示されます。必ず設定してください。
// ❌ labelなし → 凡例にundefinedが表示される
{ data: [...] }
// ✅ labelあり
{ label: '収入', data: [...] }
まとめ
| ポイント | 内容 |
|---|---|
| 月次集計 | allTransactions を filter + reduce で月ごとに集計 |
| 昇順ソート | [...uniqueMonths].reverse() でコピーしてから反転 |
| 複数データセット | datasets に収入・支出を2つ追加 |
| グラフタイトル | Title を register して options.plugins.title で設定 |
凡例の undefined |
datasets の各オブジェクトに label を必ず設定する |