chart.jsを利用してDBに登録してあるデータをグラフ描画する

## 記事のゴール

画像左上のプルダウンで指定されたアイテムの月別の金額をグラフ表示できるようになる。（UI的な部分は一旦おいといて）

![スクリーンショット 2024-11-07 22.10.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630554/0527b7b5-ed05-e2e5-b6a2-49b813cd517f.png)


## HTMLを記載

```HTML
<select id="item-name">
    <option>家賃</option>
    <option>電気代</option>
    <option>水道代</option>
</select>

<canvas id="lineChart"></canvas>
```

chart.jsを利用するには、`canvas`タグを利用する。その部分にJSで記載したグラフ情報が乗っかってくる。

## JavaScriptを記載

```JavaScript
`use strict`

// ①selectElementを取得して変数に代入
const selectElement = document.getElementById('item-name');

selectElement.addEventListener('change', function() {

    // 選択された option の値とテキストを取得
    const selectedText = selectElement.options[selectElement.selectedIndex].text;

    console.log("選択されたテキスト:", selectedText);

    retrieveItemPrice(selectedText);
})

/* ②グラフの作成 */
let lineChart = null;

let createChart = (data) => {
    let lineCtx = document.getElementById("lineChart");

    // 既存のチャートインスタンスが存在する場合は破棄
    if (lineChart) {
        lineChart.destroy();
    }

    // 線グラフの設定
    let lineConfig = {
        type: 'line',
        data: {
            labels: Object.keys(data),
            datasets: [{
                label: selectElement.options[selectElement.selectedIndex].text,
                data: Object.values(data),
                borderColor: '#f88',
            }],
        },
        options: {
            scales: {
                y: {
                    suggestedMin: 5000,
                    suggestedMax: 30000,
                    ticks: {
                        stepSize: 5000
                    }
                }
            },
        },
        responsive: true,
        maintainAspectRatio: false,
    };

    // グローバル変数 lineChart に新しいチャートインスタンスを代入
    lineChart = new Chart(lineCtx, lineConfig);
};

/* ③サーバー側へデータ通信 */
let retrieveItemPrice = (selectedText) => {
    $.ajax({
        url: "/itemPrices",
        type: "POST",
        contentType: "application/json",
        dataType: "json",
        data: JSON.stringify({ selectedText: selectedText }), // データをJSON形式で送信
    }).done(function(data) {
        console.log(data);
        createChart(data);
    }).fail(function() {
        console.log('fail');
    })
}
```


### プルダウンリストから選択された情報を取得

```JavaScript
const selectElement = document.getElementById('item-name');

selectElement.addEventListener('change', function() {

    // 選択された option の値とテキストを取得
    const selectedText = selectElement.options[selectElement.selectedIndex].text;

    console.log("選択されたテキスト:", selectedText);

    retrieveItemPrice(selectedText);
})
```

DOMを利用して、id = item-nameからElementを取得。プルダウンの変更を契機に選択されたテキストを取得。サーバー通信処理の`retrieveItemPrice()`関数の引数として渡す。


### サーバーとの通信

```Javascript
/* ③サーバー側へデータ通信 */
let retrieveItemPrice = (selectedText) => {
    $.ajax({
        url: "/itemPrices",
        type: "POST",
        contentType: "application/json",
        dataType: "json",
        data: JSON.stringify({ selectedText: selectedText }), // データをJSON形式で送信
    }).done(function(data) {
        console.log(data);
        createChart(data);
    }).fail(function() {
        console.log('fail');
    })
}
```

引数として渡された文字列をdataとしてサーバー側へ渡す。処理はjQueryを利用してのAjaxで記述。トレンドではFetchとかがあると思うので書けるならそっちの方がいいと思う。通信が成功したら取得した情報を`createChart`関数に引数として渡す。


サーバー側の処理は`Java + SpringBoot`での実装例を記載。JS側からは`@RequestBody`にてリクエストを受け取る。

```Java
    @ResponseBody
    @RequestMapping("/itemPrices")
    public Map<String, Integer> retrieveItemPrice(@RequestBody Map<String, String> request) {
        User user = (User) session.getAttribute("user");
        String selectedItemName = request.get("selectedText");

        // (年月, 金額)となっているMapを返すメソッドを想定
        return itemService.retrieveItemsByName(user.getUserId(), selectedItemName);
    }
```

DBにはこんな感じで対象年月に対しての各項目の金額が入っているテーブルを想定している。

年月| 名前 | 金額
--|--|--
2024/01 | 電気代 | 1000
2024/01 | 水道代 | 2000
2024/02 | 電気代 | 3000
2024/02 | 水道代 | 4000
2024/03 | 電気代 | 1000
2024/03 | 水道代 | 2000
2024/04 | 電気代 | 3000
2024/04 | 水道代 | 4000


### グラフの描画

サーバー側から渡されたデータを引数にグラフ描画処理を走らせる。細かい設定は色々できるので、詳しくはchart.jsの公式ページなどを参考にされたし。

```JavaScript
/* ②グラフの作成 */
let lineChart = null;

let createChart = (data) => {
    let lineCtx = document.getElementById("lineChart");

    // 既存のチャートインスタンスが存在する場合は破棄
    if (lineChart) {
        lineChart.destroy();
    }

    // 線グラフの設定
    let lineConfig = {
        type: 'line',
        data: {
            labels: Object.keys(data),
            datasets: [{
                label: selectElement.options[selectElement.selectedIndex].text,
                data: Object.values(data),
                borderColor: '#f88',
            }],
        },
        options: {
            scales: {
                y: {
                    suggestedMin: 5000,
                    suggestedMax: 30000,
                    ticks: {
                        stepSize: 5000
                    }
                }
            },
        },
        responsive: true,
        maintainAspectRatio: false,
    };

    // グローバル変数 lineChart に新しいチャートインスタンスを代入
    lineChart = new Chart(lineCtx, lineConfig);
};
```


## プルダウンの変更で動的にグラフを再描画可能

電気代選択時

![スクリーンショット 2024-11-07 22.10.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630554/0527b7b5-ed05-e2e5-b6a2-49b813cd517f.png)

↓

ガス代選択時

![スクリーンショット 2024-11-07 22.35.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2630554/9011c90a-de52-3aa6-ae0e-da304f27e59c.png)


上限値、下限値なども選択したものによって変更できるようにするとより見やすいグラフが描画できるようになると思います。