【Chart.js × Laravel】Controllerで返されたデータをJavaScriptで操作するには


Controllerで返された値をJavascriptで操作する方法、Javascriptでchart.jsを使って複数のチャートで動的なデータを表示する方法があまり見当たらなかったのでこちらで紹介。



通常Controllerで返された値(配列)は```@foreach```を使ってbladeで表示できる。

```php:test.blade.php

// as $key => $valueの場合、
@foreach($array_datas as $key => $value)
     <div class="box">
         <p>keyは{{ $key }}で、valueは{{ $value }}だよ</p>
     </div>
@endforeach

// as $dataの場合、
@foreach($array_datas as $data)
     <div class="box">
         <p>それぞれのデータは{{ $data }}として表示されたよ。</p>
     </div>
@endforeach

```
bladeでそのままデータを表示する場合は、上記のようにすれば問題ない。
JSライブラリによっては、出力される属性を```id```を指定している。
そのため```@foreach```との併用の場合少し工夫がいる。

chart.jsも同様で、出力される属性は```id```なのでそのまま```@foreach```を使うと最初だけchartが表示されて2回目以降は表示されない。これはidがユニークなものでなければならないため、重複したidは2度目以降読み込まれないから。
なので、idで設定しているものを```foreach```で回すときはHTML側で```{{$key}}```を設定し、それぞれを独立させる必要がある。また、Javascript側でも読み込めるように```forEach```を使って読み込む```id```を指定する。

まず初めに、chart.jsをそれぞれで読み込めるようにblade側を以下のように変更する。

```php:test.blade.php
@foreach($array_datas as $data)
     <div class="box">
         <canvas id="myBarChart{{ $key }}"></canvas>
     </div>
@endforeach

```
keyを追加することでユニークなidを```id=myBarChart0```、```id=myBarChart1```、```id=myBarChart2```、のように生成する。

javascript側は```document.getElementById()```でユニークなidを読み込むので、それぞれのidを読み込むように、forEachを使って要素ごとの実行を行う。
なお、```$foreach```で定義された```$array_datas```は```@JSON```を使うことでjavascriptでも操作できる。

```javascript:test.blade.php(chart.js以下にscriptを設ける)
<script>
const array_datas = @JSON($array_datas); //bladeの$array_datasをjavascriptで読み込む
const data_keys = Object.keys(array_datas); // それぞれのkeyを取得

data_keys.forEach(el =>{
  const chart_id = "myBarChart" + el; // elはdata_keysのそれぞれのkey
  var ctx = document.getElementById(chart_id);
  var myBarChart = new Chart(ctx, {
   // それぞれのidごとにchartが生成される。
  });
});
</script>
```

ここまでで```@foreach``` の中にあるchartは2回目以降も表示される。
javascript側で常に```key```をみることで、bladeの$keyと連動させることができる。

とはいえ、これでは同じチャートの情報を複製しているにすぎない。

ここからはそれぞれのchartでkeyごとに別々のデータを表示する方法を紹介。
chart.jsでは表示されるデータの値を以下のようにdatasets以下のdata配列から参照する。

```javascript:test.blade.php
var myBarChart = new Chart(ctx, {
    type: 'bar',
    data: {
      labels: ['8月1日', '8月2日', '8月3日', '8月4日', '8月5日', '8月6日', '8月7日'],
      datasets: [
        {
          label: 'A店 来客数',
          data: [62, 65, 93, 85, 51, 66, 47],
          backgroundColor: "rgba(219,39,91,0.5)"
        },{
          label: 'B店 来客数',
          data: [55, 45, 73, 75, 41, 45, 58],
          backgroundColor: "rgba(130,201,169,0.5)"
        },{
          label: 'C店 来客数',
          data: [33, 45, 62, 55, 31, 45, 38],
          backgroundColor: "rgba(255,183,76,0.5)"
        }
      ]
    },
.
.
.
```

例えば、Controllerで取得した配列データが連想配列で、そのデータがkey以下に複数の配列が存在する以下のような場合、
![スクリーンショット 2021-02-28 19.20.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596022/99d14507-308c-c9e4-896c-c7af0bff7aeb.png)

それぞれのkey(0,1,2)ごとにデータ("Aサイト" => array3)を参照してchartで表示したい。
こんなときはJavascriptでいったんkeyごとに配列を生成して、それを参照するようにchart.jsに記述する。

```javascript

const array_datas = @JSON($array_datas);
        const data_keys = Object.keys(array_datas);
        console.log(data_keys); // ["0", "1", "2"]
        data_keys.forEach(el =>{
            const month = [];
            const clicks = [];
            const imps = [];
            const chart_id = "myBarChart" + el;
            const array_data = Object.values(array_datas[el]);
            console.log(array_data);
            for(var i = 0; i < array_data[0].length; i++){
                month.push(array_data[0][i]['month']);
                clicks.push(array_data[0][i]['click']);
                imps.push(array_data[0][i]['imps']);
            }
            console.log(month); // ["2020-12", "2020-11", "2020-10"]
            console.log(clicks); // [3, 8, 1]
            console.log(imps); //[36, 52, 24]

            var ctx = document.getElementById(chart_id);
            var myBarChart = new Chart(ctx, {
                type: 'bar',
                data: {
                  labels: month,
                  datasets: [
                    {
                      label: 'クリック数',
                      data: clicks,
                      backgroundColor: "rgba(219,39,91,0.5)",
                    },{
                      label: '表示回数',
                      data: imps,
                      backgroundColor: "rgba(130,201,169,0.5)"
                    },
                  ]
                },
                options: {
                    scales: {
                      yAxes: [{ //y軸
                        ticks: {
                          suggestedMax: 80,
                          suggestedMin: 0,
                          stepSize: 10,
                        }
                      }],
                      xAxes: [{ //X軸
                        ticks: {
                          font: {
                            size: 3,
                          },
                          padding: 0
                        }
                      }]
                    },
                    layout: {
                        padding: {
                            left: 0,
                            right: 0,
                            top: 0,
                            bottom: 0
                        }
                    },
                    plugins: {
                        datalabels: { // 共通の設定はここ
                            font: {
                                size: 14,
                                color: 'rgba(200,60,60,1)',
                            },
                            anchor: 'end', 
                            align: 'end', 
                        }
                    },
                  }
                });
            
        });
```
bladeも若干調整する。

```php:test.blade.php
@foreach($array_datas as $key => $value)
     <div class="chart-box">
         <div>keyの番号{{$key}}番目のチャート情報</div>
         @foreach ($value as $index_name => $item)
         <div>~~ {{$index_name}}の結果 ~~</div>
         @endforeach
         <canvas id="myBarChart{{ $key }}"></canvas>
     </div>
@endforeach

<style>
        .chart-box {
            display: inline-block;
            text-align: center;
            width: 300px;
            margin: 30px;
            background: #f7f7f7;
        }
</style>





 <script src="{{ asset('js/Chart.bundle.js') }}"></script>
 <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-datalabels@0.7.0"></script>

```

結果、以下のようにそれぞれの値をchartで表示することができる。
![スクリーンショット 2021-02-28 19.42.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596022/d4afbc79-6f9f-a414-430c-97cea5f81b0f.png)


### おまけ
chart.jsのy軸のラベルはデフォルトでは数値を記述することで,Max,Min,Stepを設定できる。できれば、配列のデータごとにlabelも可変されるとよりみやすくなる。その場合は、下記のように関数を設定して動的にlabelを設定するといい感じのチャートが完成する。

```javascript
yAxes: [{ //y軸
         ticks: {
           suggestedMax: 80, // ←ここ
           suggestedMin: 0, 
           stepSize: 10, // ←ここ
         }
       }],

// ********* 以下のように変更 ********* //
yAxes: [{ //y軸
         ticks: {
           suggestedMax: adjastSuggestedMax(month,clicks,imps)
           suggestedMin: 0, 
           stepSize: adjastSuggestedStep(month,clicks,imps),
         }
       }],
.
.
.
});
// 以下２つの関数を追加
function adjastSuggestedMax(sc_keyword_month,sc_keyword_clicks,sc_keyword_imps){
            max_clicks = Math.max.apply(null, sc_keyword_clicks);
            max_imps = Math.max.apply(null, sc_keyword_imps);
            all_max_num = [];
            all_max_num.push(max_clicks,max_imps);

            max_num = Math.max.apply(null, all_max_num);
            if(max_num < 10){
                var suggestedMax = 10;
            } else if(10 <= max_num){
                var suggestedMax = max_num + 20;
            }
            return suggestedMax;
        };

function adjastSuggestedStep(sc_keyword_month,sc_keyword_clicks,sc_keyword_imps){
            max_clicks = Math.max.apply(null, sc_keyword_clicks);
            max_imps = Math.max.apply(null, sc_keyword_imps);
            all_max_num = [];
            all_max_num.push(max_clicks,max_imps);

            max_num = Math.max.apply(null, all_max_num);
            if(max_num < 10){
                var suggestedStep = 10;
            } else if(10 <= max_num){
                var suggestedStep = 30;
            }
            return suggestedStep;
        };
```

こうすることで配列の数字の中から最大値を検知してそれに合わせてラベルやステップを表示することができる。if条件をさらに細かくすれば自分好みの可変もできる。




### まとめ

今回はbladeのデータをJavascriptで扱う方法と個別チャートの表示方法を紹介しました。
chart.jsに限らず多くのライブラリで同じような方法で個別に生成したりできるので、他のライブラリでも挑戦してみてください。