Vue.js でダッシュボードを作成③(Vue-chart.js でのグラフ描画編)



前回の記事で 作成した Vue アプリケーションのダッシュボード画面に、グラフを描画してみました。
ライブラリは Vue でグラフ描画するためのライブラリvue-chart.jsを使いました。
また、データについてはまだサーバーとの連携をしていないので、サンプルとしてランダム関数を使って適当に作成しています。





















*Vue-chart.js とは

Chart.js を Vue で使うためのラッパーです。
グラフコンポーネントを簡単に作成することができます。


*Chart.js とは

シンプルで柔軟性に富んだグラフを作成することができる、グラフ描画ライブラリです。
HTML5のcanvas要素で様々な種類のチャートを作成することができます。


*環境

  • MacOS
  • chart.js 2.8.0
  • vue-chartjs 3.4.2


*参照



*インストール

Vueプロジェクトは下記記事で作成したものを使いました。




プロジェクトディレクトリの直下で下記コマンドを実行し、インストールします。
$ npm install vue-chartjs chart.js --save


*コンポーネント設計

下記コンポーネント設計にしました。
ダッシュボード画面からグラフカードのコンポーネントを呼び出し、そのグラフカードで折れ線グラフのコンポーネントを呼び出します。
DashBoard.vue <--  GraphCard.vue  <-- LineChart.vue
  • DashBoard ---- グラフに使うデータを取得
  • GraphCard ---- グラフのサイズや色を指定
  • LineChart ---- DashBoardGraphCardから受け取ったデータを使ってグラフを描画


*Vue-chartjs の使い方

まず、折れ線グラフのコンポーネントをLineChart.vueとして新規作成します。
vue-chartjsから作成したいグラフの種類をimportし、extendsに追加します。
次にmounted()のなかでrenderChart()を呼び出し、引数にdata, optionsのプロパティを渡すとグラフを描画することができます。
渡すデータ形式については こちらのサイト を参考にしました。

今回は呼び出し元からpropsで値を受け取るようにしたので、このコンポーネントではデータの作成はしていません。引数で渡すdata, optionsの値が変更されると再レンダリングされます。
<src/components/atoms/LineChart.vue>
<script>
import { Line } from "vue-chartjs";
export default {
  extends: Line,
  props: {
    data: Object,
    options: Object
  },
  mounted() {
    this.renderChart(this.data, this.options);
  }
};
</script>

補足ですが、dataoptionは下記形式でオブジェクトとして定義します。(必要な設定だけ追加します)
今回この設定はGraphCard.vueで行なっています。
data: {
    labels: ['2019/7/1','2019/7/2','2019/7/3','2019/7/4','2019/7/5'],    // X軸のラベル名
    datasets: [{
        data: [100, 340, 554, 10, 635],    //グラフで使うデータ
        backgroundColor: '#3280f57e        //背景色
    }]
},
options: {
    responsive: true,    //グラフサイズの自動調整
    legend: {
        display: false   //凡例の非表示
   },
    title: {
        display: true,   //タイトルの表示
        fontSize: 18,    //フォントサイズ
        text: 'タイトル'   //グラフ名表示
    },
    scales: {
        yAxes: [{
            display: true,        //Y軸の表示
            scaleLabel: {
               display: true,     //Y軸のラベル表示
               labelString: 'Y',  //Y軸のラベル
               fontSize: 18       //Y軸のラベルのフォントサイズ
            },
            ticks: {
                min: 0,           //Y軸の最小値
                max: 30,          //Y軸の最大値
                fontSize: 18,     //Y軸のフォントサイズ
                stepSize: 5       //Y軸の間隔
            },
        }],
        xAxes: [{
            display: true,        //X軸の表示
            scaleLabel: {
               display: true,     //X軸の表示
               labelString: 'X',  //X軸のラベル
               fontSize: 18       //X軸のラベルのフォントサイズ
            },
            ticks: {
                fontSize: 18      //X軸のフォントサイズ
            },
        }],
    },
    layout: {
        padding: {
            left: 100,
            right: 50,
            top: 0,
            bottom: 0
        }
    }
}


*子コンポーネントの呼び出し

GraphCard.vueから上で作成したLineChart.vueを呼び出します。
LineChart.vueをインポートし、<template>で表示させたい箇所に<line-chart>を追加します。このとき、heightwidthのプロパティを設定することで、表示するグラフのサイズを変更することができます。

このコンポーネントでグラフの色やオプションの定義をしています。
今回は一時的なサンプルデータを使ったので、グラフの Y軸の最小値、最大値、間隔を指定しましたが、ここは使うデータによって任意の値に修正します。(オプションを何も設定しなくてもグラフの描画はできます)
<src/components/molecules/GraphCard.vue>
<template>
  <div class="graph">
    <card-title :value="name"></card-title>
    <!---- ↓追加 ---->
    <line-chart class="chart" :data="getChartData()" :options="getOptions()" :height="270" :width="770"></line-chart>
  </div>
</template>
<script>
import CardTitle from "../atoms/CardTitle";
import LineChart from "../atoms/LineChart";    //←追加
export default {
  components: {
    CardTitle,
    LineChart        //←追加
  },
  props: {
    name: String,
    data: Array,     //←追加
    label: Array     //←追加
  },
  <!---- ↓追加 start ---->
  data() {
    return {
      graphData: null
    };
  },
  methods: {
    getChartData() {
      return {
        labels: this.label,
        datasets: [
          {
            data: this.data,
            borderColor: "#3280f57e",
            backgroundColor: "#3280f528",
            animation: false
          }
        ]
      };
    },
    getOptions() {
      // TODO: fix min and max value
      return {
        legend: {
          display: false
        },
        scales: {
          yAxes: [
            {
              ticks: {
                min: 200,
                max: 600,
                stepSize: 100
              }
            }
          ]
        }
      };
    }
    <!---- ↑追加 end ---->
  }
};
</script>
<style scoped lang="scss">
.graph {
  display: inline-block;
  margin: 0 0 40px 40px;
  height: 300px;
  width: 770px;
  background-color: white;
  border: 0 solid #aaa;
  border-radius: 4px;
  box-shadow: 2px 2px 2px 2px #ccc;
  padding: 15px;
}
// ↓追加
.chart {
  padding-top: 15px;
}
</style>

さらにダッシュボード画面からカードのコンポーネントを呼び出します。Dashboard.vueGraphCard.vueをインポートする処理を追加します。
Dashboard.vueではグラフに使うデータを作成して子コンポーネントに渡しています。

Math.floor(Math.random() * Math.floor())でランダムな整数を作成します。さらに整数の範囲を 300 〜 500 の値にしたかったため、300 + Math.floor(Math.random() * Math.floor(500 - 300))としています。
このランダムな整数を 30日分(1ヵ月)作成したかったので、30回分ループさせるために[...Array(30)]map()を使いました。
const result = [...Array(30)].map(
        () => 300 + Math.floor(Math.random() * Math.floor(500 - 300)))

また、X軸のラベルに今月の日時を表示させたかったので、今月の初日を取得し、1日ずつ加算した日付の文字列を配列にして作成しています。
const date = new Date();
date.setDate(0);
const result = [...Array(30)].map(() => {
  date.setDate(date.getDate() + 1);
  return date.toLocaleDateString();
});

Dashboard.vueの全体は下記になりました。
<src/components/pages/Dashboard.vue>
<template>
  <div>
    <navbar></navbar>
    <sidebar></sidebar>
    <div class="top">
      <div>
        <value-card name="ユーザー数 合計" value="256"></value-card>
        <value-card name="プレビュー数 合計" value="21,234"></value-card>
      </div>
      <!---- ↓ data,labelを追加 ---->
      <graph-card name="ユーザー数 推移" :data="getUserData" :label="getLabelList"></graph-card>
      <graph-card name="プレビュー数 推移" :data="getPreviewData" :label="getLabelList"></graph-card>
      <list-card name="データ一覧"></list-card>
    </div>
  </div>
</template>
<script>
import Navbar from "../organisms/Navbar";
import Sidebar from "../organisms/Sidebar";
import ValueCard from "../molecules/ValueCard";
import GraphCard from "../molecules/GraphCard";
import ListCard from "../molecules/ListCard";
export default {
  components: {
    Navbar,
    Sidebar,
    ValueCard,
    GraphCard,
    ListCard
  },
  <!---- ↓追加 start ---->
  computed: {
    getUserData() {
      // TODO: fix getting from server data
      return [...Array(30)].map(
        () => 300 + Math.floor(Math.random() * Math.floor(500 - 300))
      );
    },
    getLabelList() {
      const date = new Date();
      date.setDate(0);
      return [...Array(30)].map(() => {
        date.setDate(date.getDate() + 1);
        return date.toLocaleDateString();
      });
    },
    getPreviewData() {
      // TODO: fix getting from server data
      return [...Array(30)].map(
        () => 300 + Math.floor(Math.random() * Math.floor(500 - 300))
      );
    }
  }
  <!---- ↑追加 end ---->
};
</script>
<style scoped lang="scss">
.top {
  padding: 66px 0 0 200px;
}
</style>


















*所感

グラフ描画をコンポーネント化できるので、1度作成してしまえば設定を変更したグラフもすぐ作成できて便利でした。ただ、データを作成する箇所を間違えると汎用的に使えなくなってしまうので、設計はよく考慮する必要がありそうです。


Previous
Next Post »

人気の投稿