Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue Chart.js - Chart is not updating when data is changing

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)     } }) 
like image 948
Léo Coco Avatar asked May 02 '17 00:05

Léo Coco


1 Answers

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/[email protected]/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://vuejs.org/v2/guide/components.html#Prop-Validation

Here is a working codepen https://codepen.io/azs06/pen/KmqyaN?editors=1010

like image 75
azs06 Avatar answered Sep 18 '22 14:09

azs06