I’m using Vue.js and Chart.js to draw some charts.
Each time I call the function generateChart()
, the chart is not updated automatically. When I check the data in Vue Devtools, they are correct but the chart does not reflect the data. However, the chart does update when I resize the window.
- What is wrong with what I’m doing?
- How do I update the chart each time I call
generateChart()
?
I feel this is going to be something related with object
and array
change detection caveats, but I’m not sure what to do.
https://codepen.io/anon/pen/bWRVKB?editors=1010
<template> <el-dialog title="Chart" v-model="showGeneratedChart"> <line-chart :chartData="dataChart"></line-chart> </el-dialog> </template> <script> export default { data() { const self = this; return { dataChart: { labels: [], datasets: [ { label: "label", backgroundColor: "#FC2525", data: [0, 1, 2, 3, 4], }, ], }, }; }, methods: { generateChart() { this.dataChart["labels"] = []; this.dataChart["datasets"] = []; // ... compute datasets and formattedLabels this.dataChart["labels"] = formattedLabels; this.dataChart["datasets"] = datasets; }, }, }; </script>
LineChart.js
import { Line, mixins } from 'vue-chartjs' export default Line.extend({ mixins: [mixins.reactiveProp], props: ["options"], mounted () { this.renderChart(this.chartData, this.options) } })
Advertisement
Answer
Use a computed property for the chart data. And instead of calling this.renderChart
on watch wrap it in a method and reuse that method on mounted
and in watch
.
Vue.component("line-chart", { extends: VueChartJs.Line, props: ["data", "options"], mounted() { this.renderLineChart(); }, computed: { chartData: function() { return this.data; } }, methods: { renderLineChart: function() { this.renderChart( { labels: [ "January", "February", "March", "April", "May", "June", "July" ], datasets: [ { label: "Data One", backgroundColor: "#f87979", data: this.chartData } ] }, { responsive: true, maintainAspectRatio: false } ); } }, watch: { data: function() { this._chart.destroy(); //this.renderChart(this.data, this.options); this.renderLineChart(); } } }); var vm = new Vue({ el: ".app", data: { message: "Hello World", dataChart: [10, 39, 10, 40, 39, 0, 0], test: [4, 4, 4, 4, 4, 4] }, methods: { changeData: function() { this.dataChart = [6, 6, 3, 5, 5, 6]; } } });
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Vue.jS Chart</title> </head> <body> <div class="app"> {{ dataChart }} <button v-on:click="changeData">Change data</button> <line-chart :data="dataChart" :options="{responsive: true, maintainAspectRatio: false}"></line-chart> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.2.6/vue.min.js"></script> <script src="https://unpkg.com/vue-chartjs@2.5.7-rc3/dist/vue-chartjs.full.min.js"></script> </body> </html>
You could also make the options a computed property, and if option not going to change much you can setup default props. https://v2.vuejs.org/v2/guide/components.html#Prop-Validation
Here is a working codepen https://codepen.io/azs06/pen/KmqyaN?editors=1010