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

Laravel ※Web開発
【Chart.js × Laravel】Controllerで返されたデータをJavaScriptで操作するには - Qiita
Controllerで返された値をJavascriptで操作する方法、Javascriptでchart.jsを使って複数のチャートで動的なデータを表示する方法があまり見当たらなかったのでこちらで紹介。…

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

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

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側を以下のように変更する。

test.blade.php

@foreach($array_datas as $data)
     <div class="box">
         <canvas id="myBarChart{{ $key }}"></canvas>
     </div>
@endforeach

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

javascript側はdocument.getElementById()でユニークなidを読み込むので、それぞれのidを読み込むように、forEachを使って要素ごとの実行を行う。
なお、$foreachで定義された$array_datas@JSONを使うことで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配列から参照する。

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以下に複数の配列が存在する以下のような場合、

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


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も若干調整する。

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で表示することができる。

おまけ

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

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

// ********* 以下のように変更 ********* //
yAxes: [{ //y軸
         ticks: {
           suggestedMax: adjastSuggestedMax(month,clicks,imps)
           suggestedMin: 0, 
           stepSize: adjastSuggestedStep(month,clicks,imps),
         }
       }],
.
.
.
});
// 以下2つの関数を追加
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に限らず多くのライブラリで同じような方法で個別に生成したりできるので、他のライブラリでも挑戦してみてください。

タイトルとURLをコピーしました