I am working on a nuxt project and need to draw some charts. For that I use vue-chartjs as a plugin. Unfortunately the chart-data is fetched after the chart is drawn which results in an empty chart. I can't find a way to access the plugin and rerender the chart when the data is ready. Here is the code for the plugin:
// plugins/vue-chart.js
import Vue from 'vue'
import { Bar, Doughnut, Line, Pie } from 'vue-chartjs'
const registerComponent = function (name, originalComponent) {
Vue.component(
name,
{
extends: originalComponent,
props: ['data', 'options'],
mounted () {
this.renderChart(this.data, this.options)
},
updated () {
this.renderChart(this.data, this.options)
}
}
)
}
registerComponent('BarChart', Bar)
registerComponent('DoughnutChart', Doughnut)
registerComponent('LineChart', Line)
registerComponent('PieChart', Pie)
And here is how I use the plugin to draw a line chart:
components/Charts.vue
<template>
<client-only>
<LineChart :data="lineData" :options="options" />
</client-only>
</template>
<script>
export default {
data() {
return {
loading: true,
lineData: {
labels: [],
datasets: [
{
label: 'Close',
data: [],
},
{
label: 'High',
data: [],
},
],
},
options: {
responsive: true,
scales: {
x: {
display: true,
title: {
display: true,
},
},
y: {
display: true,
title: {
display: true,
text: 'Value',
},
},
},
},
}
},
async mounted() {
try {
const response = await this.$axios.get('/api/entries')
if (response.status === 200) {
for (let i = 0; i < response.data.length; i ) {
this.lineData.labels.push(response.data[i].date)
this.lineData.datasets[0].data.push(response.data[i].close)
this.lineData.datasets[1].data.push(response.data[i].high)
}
}
} catch (e) {
console.log(e)
}
},
}
</script>
I am happy about any suggestions on how to solve this!
Edit
I had to downgrade the versions of chart.js
and vue-chartjs
so that the project compiles.
Here is my package.json
{
"name": "xyz",
"version": "1.0.0",
"private": true,
"scripts": {
"dev-fe": "nuxt",
"build": "nuxt build",
"start": "nuxt start",
"generate": "nuxt generate",
"lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .",
"lint": "npm run lint:js"
},
"dependencies":
"@nuxtjs/axios": "^5.13.6",
"chart.js": "^2.7.1",
"core-js": "^3.15.1",
"nuxt": "^2.15.7",
"vue": "^2.6.14",
"vue-chartjs": "^3.4.0"
},
"devDependencies": {
"@babel/eslint-parser": "^7.14.7",
"@nuxtjs/eslint-config": "^6.0.1",
"@nuxtjs/eslint-module": "^3.0.2",
"@nuxtjs/tailwindcss": "^4.2.0",
"eslint": "^7.29.0",
"eslint-plugin-nuxt": "^2.0.0",
"eslint-plugin-vue": "^7.12.1",
"postcss": "^8.3.5"
}
}
I use pretty much use the default in nuxt.config.js
except that I added
{ src: '~/plugins/vue-chart.js', mode: 'client' }
to the plugins.
CodePudding user response:
Alright, I got a working example with the following.
Here is my vue-chartjs.js
plugin
import Vue from 'vue'
import { Bar, Doughnut, Line, Pie, mixins } from 'vue-chartjs'
const registerComponent = function (name, originalComponent) {
Vue.component(name, {
extends: originalComponent,
mixins: [mixins.reactiveProp],
props: {
chartData: {
type: Object,
default: () => {},
},
chartOptions: {
type: Object,
default: () => {},
},
},
mounted() {
this.renderChart(this.chartData, this.chartOptions)
},
})
}
registerComponent('BarChart', Bar)
registerComponent('DoughnutChart', Doughnut)
registerComponent('LineChart', Line)
registerComponent('PieChart', Pie)
/pages/index.vue
<template>
<div>
<line-chart
:key="updated"
:chart-data="lineData"
:chart-options="options"
/>
</div>
</template>
<script>
import FakeData from '@/fake.json'
export default {
data() {
return {
updated: 0,
lineData: {
labels: [],
datasets: [
{
label: 'Data One',
backgroundColor: '',
data: [],
},
{
label: 'Data Two',
backgroundColor: '',
data: [],
},
],
},
options: {
responsive: true,
scales: {
x: {
display: true,
title: {
display: true,
},
},
y: {
display: true,
title: {
display: true,
text: 'Value',
},
},
},
},
}
},
async fetch() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos/1')
const data = await response.json()
console.log('fake API title fetched:', data.title)
const actualData = FakeData.data
for (let i = 0; i < actualData.length; i ) {
this.lineData.labels.push(actualData[i].date)
this.lineData.datasets[0].backgroundColor = actualData[i].color1
this.lineData.datasets[0].data.push(actualData[i].close)
this.lineData.datasets[1].backgroundColor = actualData[i].color2
this.lineData.datasets[1].data.push(actualData[i].high)
}
this.updated
},
}
</script>
and my fake .json
API data, because I didn't have an API to get some real data
{
"data": [
{
"date": "Jan",
"color1": "#EC368D",
"color2": "#51E5FF",
"close": "0.4",
"high": "0.7"
},
{
"date": "Feb",
"color1": "#EC368D",
"color2": "#51E5FF",
"close": "0.2",
"high": "0.5"
},
{
"date": "Mar",
"color1": "#EC368D",
"color2": "#51E5FF",
"close": "0.6",
"high": "0.8"
}
]
}
I still mocked an API call, so it should work great as an example for you.