Search code examples
vue.jsvuejs3

How to update charts from parent in vue3 with vue-chartjs 5.2


I'm new to vue and to be honest I don't understand how I can update a graph from its paren. With the vue-dev-tools I see that the data got updated, but doesn't trigger a graph update. I find a lot of out-dated information for vue2 and older vue-chartjs api calls + the documentation for the new api for vue-chartjs, which doesn't help me. The vue-chartjs says that there are data watchers. So I assume, when I change the data, the graph updates. But something I'm missing here.

Codesandbox: https://codesandbox.io/s/v8rdgz

I'm using vue3 with

[email protected]
[email protected]

I fiddled around with props, computed, refs but I didn't achieve my goal. Here my minimal example. When the Update Graph Button is pressed. There should appear another line., another line should appear.

// LineChart.vue
<template>
  <Line :data="data" :options="options" />
</template>

<script>
import { Line } from "vue-chartjs";
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend,
} from "chart.js";

ChartJS.register(
  CategoryScale,
  LinearScale,
  PointElement,
  LineElement,
  Title,
  Tooltip,
  Legend
);

export default {
  components: { Line },
  props: ["data", "options"],
};
</script>

// App.vue
<template>
  <div>
    <button @click="updateGraph">Update Graph</button>
  </div>
  <div>
    <LineChart :data="chartData" :options="chartOptions" />
  </div>
</template>

<script>
import LineChart from "./components/LineChart.vue";

export default {
  data() {
    return {
      chartData: {
        labels: [
          "January",
          "February",
          "March",
          "April",
          "May",
          "June",
          "July",
        ],
        datasets: [
          {
            label: "Data One",
            backgroundColor: "#f87979",
            data: [40, 39, 10, 40, 39, 80, 40],
          },
        ],
      },
      chartOptions: {
        responsive: true,
        maintainAspectRatio: false,
      },
    };
  },
  components: {
    LineChart,
  },

  methods: {
    updateGraph() {
      this.chartData.datasets.push({
        label: "Data Two",
        backgroundColor: "#f87979",
        data: [50, 59, 50, 50, 59, 50, 50],
      });
    },
  },
};
</script>

Thank you in advance!!! Best regards


Solution

  • The problem is the update you're making is too deep. You need to replace chartData with an updated version of itself, instead of simply updating its datasets. chart.js does support mutating the datasets array, but Vue does not watch data updates deeply. So unless you replace this.chartData, Vue won't update Line.vue, used internally by LineChart.vue.

        updateGraph() {
          this.chartData = {
            ...this.chartData,
            datasets: [
              ...this.chartData.datasets, {
                label: "Data Two",
                backgroundColor: "#f87979",
                data: [50, 59, 50, 50, 59, 50, 50],
              }
            ]
          }
        }
    

    Working demo.


    Alternatively, you can tell Vue when the component needs to be re-rendered, by using :key="chartData.datasets.length" on your <LineChart /> component. However, this method re-renders the chart from scratch, so you'll get the initial data animations, not the ones from old data state to new data state.

    I recommend the first method.