
ダッシュボードを作成するのに、カード型のUIでグラフ表示をしてみたかったので、
VuetifyとChart.jsで簡単な実装をしてみました。
対象読者
以下の内容を書いてます。
- Vuetifyのカードコンポーネント実装
- そこに、Chart.jsの埋め込み
- Nuxt.JSのレンダリングタイミングの制御
できたもの
準備
create-nuxt-app
でプロジェクトを作成します。
参考 インストール - NuxtJS
$ yarn create nuxt-app <project-name>
もしくは、GitHubにプロジェクトソースを置いてるので、それを利用してください。
今回は、Vuetifyベースでの記述になります。
Nuxt.JSでChart.jsを利用するために、Chart.jsと、vue-chart.jsをインストールします。
yarn add vue-chartjs chart.js
データをベタガキ
まずは、Chart.jsの設定に直接値を書き込みます。
components/charts/chart-card.vue
グラフ部分作成
まず,Chart.jsのレンダリングをするコンポーネントを記述します。
グラフを描画したい時は、このコンポーネントを呼び出します。
components/charts/chart-card/linechart.vue
<script>
import { Line } from 'vue-chartjs';
export default {
extends: Line,
name: 'chart',
props: ['chartData', 'options'],
mounted() {
this.renderChart(this.chartData, this.options)
}
}
</script>
カード作成 + グラフ描画
components/charts/chart-card/chart-card.vue
Vuetifyの、v-card
でカード部分を実現。
メインコンテンツを実装するv-card-text
に、グラフ部分のコンポーネントを配置します。
template部
<template>
<v-card min-width="80%">
<v-card-title>
Simple Chart
</v-card-title>
<v-card-text class="chart-container">
<Chart :chartData="chartData" :options="options"/>
</v-card-text>
</v-card>
</template>
script部
data()で、グラフ描画に必要な情報を記述
-
chartData
- labels: X軸のラベル (Array)
- datasets
- label: 各線の凡例
- data: データの数値
- fill: 線グラフより下を塗りつぶすか
- backgroundColor: 塗りつぶす色
- borderColor: 線の色
-
options
- responsive: レスポンシブのOn/Off
- maintainAspectRatio: 縦横比を自動調整するか
- legend: 凡例の設定
<script>
import Chart from './linechart.vue';
export default {
components: {
Chart
},
data() {
return {
chartData: {
labels: ['A', 'B', 'C'],
datasets: [{
label: "sample",
data: [100, 150, 300],
fill: true,
backgroundColor: 'rgba(83, 224, 156, 0.2)',
borderColor: '#53e09c'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: true
},
}
}
},
}
</script>
これをpageで描画
作成した、カードコンポーネントをpageで描画します。
<template>
<div>
<v-row>
<v-col cols="12" sm="12" md="6" lg="4">
<chartCard />
</v-col>
</v-row>
</div>
</template>
<script>
import chartCard from '~/components/charts/chart-card/chart-card.vue';
export default {
components: {
chartCard,
}
}
</script>

Vue Storeからの取得
components/charts/chart-card-withStore.vue
ベタガキで使うタイミングなんてそんなにないので、
よくNuxtで用いるパターンとして、Vuexを使うパターンで実装します。
ベタ描き部分との差分のみ記述します
Store (Vuex)を作成
store/chart.js
に、Chart用のStoreを作成。
参考
export const state = () => ({
simple: {
label: ['AA', 'AB', 'AC'],
data: [1000, 2000, 2500]
},
})
カード作成 + グラフ描画
this.$store.state...
で、NuxtのどこからでもStoreの値にアクセス可能なので、
chartDataの、labelsとdataの情報を、Storeから引っ張ってくるように記述。
+++
: 前の手順からの追記箇所
...
<script>
import Chart from './linechart.vue';
export default {
(...)
data() {
return {
(...)
chartData: {
+++ labels: this.$store.state.charts_store.simple.label,
datasets: [{
label: "sample",
+++ data: this.$store.state.charts_store.simple.data,
fill: true,
backgroundColor: 'rgba(235, 70, 52, 0.2)',
borderColor: '#eb4634',
}]
},
(...)
}
</script>
pageに追記すると
+++
: 前の手順からの追記箇所
(...)
: 省略
<template>
<div>
<v-row>
(...)
+++ <v-col cols="12" sm="12" md="6" lg="4">
+++ <storeChartCard />
+++ </v-col>
</v-row>
</div>
</template>
<script>
(...)
+++ import storeChartCard from '~/components/charts/chart-card/chart-card-withStore.vue';
export default {
components: {
chartCard,
+++ storeChartCard,
}
}
</script>
これで、Storeの情報を使って、グラフ描画できました。

外部APIでの実行
components/charts/chart-card-withAPI.vue
Webアプリで一番使うのが、外部APIから情報を取得して、画面に描画する方法だと思います。なのでそのパターンを実装してみましょう。
今回は、日経平均株価のサンプルでやるために、Quandlで取得してみます。
RuntimeConfig | 外部APIのアクセストークン管理
APIを活用する時に、一番問題なのは、Nuxt.jsにToken情報などが晒されてしまう問題なので、今回は、Nuxt.jsのRuntimeConfigを使います、
かなり重要
本来は、privateRuntimeConfig
を使うのですが、SSR化が必要だったりと色々設定が必要なので、今回は簡単するために、publicRuntimeConfig
で実装します。ブラウザでAPIリクエストするので、アクセストークンは見えてしまう実装となります。完全に隠す必要がある場合は、SSR化して、privateRuntimeConfigで実装する必要があります。
publicRuntimeConfigの設定
APIアクセストークンを管理するために以下の手順を取ります。
- 環境変数ファイル '.env'を作成
- .envファイルの中を
nuxt.config.js
から読み込み
環境変数ファイル作成
Nuxt.jsにおいて、環境変数情報は、.envファイルで管理します。
QUANDL_KEY=<Quandlのアクセストークン>
そして、nuxt.config.js
に、 process.env.変数名
で .envファイルの中見を参照できます。
publicRuntimeConfig: {
quandlKey: process.env.QUANDL_KEY | <トークンベタガキ>
},
利用方法
コンポーネント等から、this.$config.quandlKey
とすることで、環境変数の値を参照することができます。
カード作成 + グラフ作成
コード全体
===
: 前の手順からの変更箇所
+++
: 前の手順からの追記箇所
(...)
: 省略
コード全体
<template>
<v-card min-width="80%">
(...)
=== <Chart v-if="loaded" :chartData="chartData" :options="options"/>
=== <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" />
</v-card-text>
</v-card>
</template>
<script>
+++ import axios from 'axios';
import Chart from './linechart.vue';
export default {
(...)
data() {
return {
+++ loaded: false,
chartData: {
=== labels: null,
datasets: [{
label: "average value",
=== data: null,
fill: true,
backgroundColor: 'rgba(232, 229, 74, 0.2)',
borderColor: '#e8e54a',
}]
},
(...)
}
},
+++ async created() {
+++ const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey;
+++ const response = await axios.get(url);
+++
+++ const data = response.data.dataset_data.data;
+++ const last_7day = data.slice(0, 7).reverse();
+++
+++ // // 直近7日の日付 + 終値
+++ const days = last_7day.map(item => item[0]);
+++ const last = last_7day.map(item => item[4]);
+++
+++ await this.$set(this.chartData, 'labels', days);
+++ await this.$set(this.chartData.datasets[0], 'data', last);
+++
+++ this.loaded = true;
+++
+++ },
}
</script>
APIからの情報を取得
まず、画面描画前にデータを取得する処理を実行するために、created()
関数内でAPIを実行します。
created()
関数は、画面のレンダリング前に実行した処理を記載します。
どのタイミングで、created()が呼ばれるかは、Nuxt Lifecycleにわかりやすいイラストがあります。
created() 内では、以下の処理を行っています.
- axios.getで日経平均株価データ取得
- レスポンスデータから、直近7日の情報を取得(slice)、日付の早い順にソート
- map関数で、日付・終値のArrayを抽出
- storeに登録
- 情報取得処理が完了したフラグを立てる
url内で、APIキーを付与するので、 this.$config.quandlKey
でRuntimeConfigから、呼び出しています。
async created() {
const url = "https://www.quandl.com/api/v3/datasets/CHRIS/CME_NK2/data.json?api_key=" + this.$config.quandlKey;
const response = await axios.get(url);
const data = response.data.dataset_data.data;
const last_7day = data.slice(0, 7).reverse();
// // 直近7日の日付 + 終値
const days = last_7day.map(item => item[0]);
const last = last_7day.map(item => item[4]);
await this.$set(this.chartData, 'labels', days);
await this.$set(this.chartData.datasets[0], 'data', last);
this.loaded = true;
},
データ取得まで、レンダリングを待つ
Chart.jsは、templateのレンダリング次にデータが固定されるので、
- created()で、情報を取得仕切る前に、グラフ部分のレンダリングが行われる
- 結果、空白のグラフが生成される
という問題があります。なので、以下の対策を取ります。
- データの取得が完了するかどうかを判断する変数、
loaded
を定義 - templateの<Chart>部分で、
v-if
を用いてレンダリング制御 - created()の末尾で、loaded = trueに変更
v-if
で、false状態にして、描画対象から外すことで、初回のレンダリング時に空で描画されるのを回避して、 v-if
がtrue状態になった時に、Chartのレンダリングが実行されるようになります。
template
レンダリング制御のために、 <Chart>に、v-if
を追記します。
また、読み込み中にローディング表示したいので、Vuetifyの v-progress-circular
を、v-else-if
で表示するようにします。
<template>
<v-card min-width="80%">
....
=== <Chart v-if="loaded" :chartData="chartData" :options="options"/>
=== <v-progress-circular v-else-if="loaded!=true" indeterminate color="#53e09c" />
</v-card-text>
</v-card>
</template>
pageに追記すると
API実行中は、ローディング画面になり、
データの読み込みが終わったらグラフ描画が走るようになります。
+++
: 前の手順からの追記箇所
(...)
: 省略
<template>
<div>
<v-row>
(...)
+++ <v-col cols="12" sm="12" md="6" lg="4">
+++ <outsideAPIChartCard />
+++ </v-col>
</v-row>
</div>
</template>
<script>
(...)
+++ import outsideAPIChartCard from '~/components/charts/chart-card/chart-card-withAPI.vue';
export default {
components: {
chartCard,
storeChartCard,
+++ outsideAPIChartCard,
}
}
</script>
と実装できました。
まとめ
今回は、Vuetify + Chart.jsで、ダッシュボードに使えそうなグラフのコンポーネントを作成してみました。
今後。GitHubにギャラリー的にまとめようと考えています。
Source Code