axiosを使って、バックエンドから取得した情報をもとにグラフを生成する方法。
結論とてはasync mounted上でawaitありでapiを呼び出せばできる。
ジャンプできる目次
インストール方法
わかる人は飛ばして。
プロジェクトディレクトリ上で、
npm install vue-chartjs@3.4.2
npm install chart.js@2.8
を叩く。
コンパイル時やブラウザ上でエラーになる場合
Cannot find module 'vue-chartjs’
とのエラーが表示される場合は、バージョンを指定せずにインストールしてしまった可能性がある。
npm uninstall vue-chartjs
npm uninstall chart.js
npm install vue-chartjs@3.4.2
npm install chart.js@2.8
と叩いて、インストールし直せば直る。
こういうオブジェクトがあるとする
今回はapi/getScore
を呼び出したら上記のような結果が返ってくると仮定して話を進める。
グラフ描画コンポーネント作成
//プロジェクトディレクトリ\resources\js\components\layout\ChartPie.vue
<script>
import {
Pie
} from 'vue-chartjs'
export default {
extends: Pie,
async mounted() {
const operators = await axios.get("/api/getScore").then(response => {
return response.data
})
const id = []
const name = []
const count = []
operators.forEach((operator, index) => {
name.push(operator.name)//オペレーターの名前群
count.push(operator.count)//オペレーターの対応件数群
});
const chartData= {
labels: name,//オペレーター名群
datasets: [{
label: 'Data One',//わからん
//それぞれのオペレーターごとの配色(結果より多くても問題ない)
backgroundColor: ['#fdcb6e', '#e17055', '#d63031','#e84393','#2d3436','#ffeaa7','#fab1a0','#ff7675','#fd79a8','#636e72','#00b894','#00cec9','#0984e3','#6c5ce7','#b2bec3','#55efc4','#81ecec','#74b9ff','#a29bfe','#dfe6e9'], //オペレーターの人数分だけ用意する
data: count //そのオペレーターの対応件数
}]
}
const options = {
responsive: true,
maintainAspectRatio: false
}
this.renderChart(chartData, options)//情報をもとにグラフを生成
}
}
</script>
async mounted()
内でawait
付きでapiを実行することにより、
結果が取得できてから続きの処理を行うことになる。
このようにしないと、情報が取れる前にグラフの描画が始まって結果的に何も表示されなくなる。
ちなみに配色は、
Flat UI Colors 2 - 14 Color Palettes, 280 colors 🎨
の内容をてきとうにコピーしてきた。
親コンポーネント作成
さっき作ったコンポーネントを読むだけ。
//Dashboard.vue
<template>
<div>
<h1>ダッシュボード</h1>
<template>
<ChartPi></ChartPi>
</template>
</div>
</template>
<script>
import ChartPi from '../layout/ChartPie.vue'//グラフ描画コンポーネントまでのパス
export default {
components: {
ChartPi
},
title() {
return this.title
},
}
</script>
import ChartPi from '../layout/ChartPie.vue'
は自分の環境に合わせる必要がある。
これで終わり。
ダッシュボードを表示したらグラフが出てくるはず。
APIに渡す条件も動的に変更したい場合は?
例えばこんな風に日付を選んで集計できるようにする場合など。
親コンポーネントからpropsで値を子コンポーネント(グラフ)に渡す
↓
受け取ったpropsを子コンポーネントからapiに投げる
↓
親コンポーネント上で集計ボタンが押されるたびに子コンポーネント(グラフ)を再レンダリングする
親コンポーネント
<template>
<div>
<template>
<v-card>
<v-card-text>
<v-row>
<v-col cols="12" sm="3" md="4">
<v-menu v-model="postDateMenu.start" :close-on-content-click="false" :nudge-right="40"
transition="scale-transit12ion" offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="dates[0]" label="開始" prepend-icon="mdi-calendar" readonly
v-bind="attrs" v-on="on">
</v-text-field>
</template>
<v-date-picker v-model="dates[0]" locale="jp-ja"
:day-format="date => new Date(date).getDate()" @input="postDateMenu.start = false">
</v-date-picker>
</v-menu>
</v-col>
<v-col cols="12" sm="3" md="4">
<v-menu v-model="postDateMenu.stop" :close-on-content-click="false" :nudge-right="40"
transition="scale-transition" offset-y min-width="auto">
<template v-slot:activator="{ on, attrs }">
<v-text-field v-model="dates[1]" label="終了" prepend-icon="mdi-calendar-clock"
readonly v-bind="attrs" v-on="on" >
</v-text-field>
</template>
<v-date-picker v-model="dates[1]" locale="jp-ja"
:day-format="date => new Date(date).getDate()" @input="postDateMenu.stop = false">
</v-date-picker>
</v-menu>
</v-col>
<v-col cols="12" sm="3" md="4">
<div class="pt-4" />
<v-btn class="" @click="submit" color="primary">集計</v-btn>
</v-col>
</v-row>
</v-card-text>
</v-card>
<v-card-text>
<v-card>
<v-card-title>機種別</v-card-title>
<PerType v-if="resetFlag" :dates="dates"></PerType>
</v-card>
</v-card-text>
</template>
</div>
</template>
<script>
import PerType from '../layout/PerType.vue'
export default {
components: {
PerType,
},
data() {
return {
postDateMenu: {
start: false,
stop: false
},
dates: [
"2022-11-01",
"2022-12-01",
],
resetFlag: true,
}
},
methods: {
submit() {
if (this.dates[0] > this.dates[1]) {
this.dates = this.dates.reverse()
}//検索開始日が終了日より未来を選択してしまっていたら修正する
//集計のたびにグラフを再読み込みする
this.resetFlag = false
this.$nextTick(() => (this.resetFlag = true))
},
}
}
</script>
submit()
関数内の
this.resetFlag = false
this.$nextTick(() => (this.resetFlag = true))
が重要。
集計のたびにグラフを再読み込みするので、「子コンポーネントが変数の変化に気づかずグラフが変化しない」
なんてことはなくなる。
子コンポーネント
//PerType.vue
<script>
import {
Pie
} from 'vue-chartjs'
export default {
props:{
dates:{
type:Array,
}
},
extends: Pie,
async mounted() {
const records = await axios.get("/api/PerType?dates="+this.dates).then(response => {
return response.data
})
const id = []
const name = []
const count = []
records.forEach((record, index) => {
id.push(record.id)
name.push(record.name)
count.push(record.count)
});
const chartData= {
labels: name,
datasets: [{
label: '機種別',
backgroundColor: ['#fdcb6e', '#e17055', '#d63031','#e84393','#2d3436','#ffeaa7','#fab1a0','#ff7675','#fd79a8','#636e72','#00b894','#00cec9','#0984e3','#6c5ce7','#b2bec3','#55efc4','#81ecec','#74b9ff','#a29bfe','#dfe6e9'],
data: count
}]
}
const options = {
responsive: true,
maintainAspectRatio: false
}
this.renderChart(chartData, options)
}
}
</script>
propsで親から変数dates
(2つの日付が入った配列)を受け取る
↓
async mounted(){ }
内で、その値を使てAPIを呼び出す。
awaitを使ってちゃんと値が取れるのを待つ。
const records = await axios.get("/api/PerType?dates="+this.dates)~~~
↓
APIの結果が返ってきたらその情報をもとにグラフを生成する。
こんな感じ。
Vue公式のドキュメントによると
もし Vue で強制更新をする必要な場面に遭遇する場合、99.99% のケースであなたは何かを間違えています。
特別な問題に対処する — Vue.js
だまれ