Search code examples
javascriptvue.jschartshighchartsvuejs3

Vue 3 how to correctly update data in Highcharts?


Trouble updating data in Highcharts with Vue.js 3

I have a small webapp with Vue.js 3 that shows a Highcharts Chart and some statistics, with global buttons for time-filters (All, Year, Month, Week).
The data of my Highchart chart needs to change, whenever one of the global buttons was pressed.
I used this vue3 wrapper for Highcharts: Wrappers Github

Displaying the initial data (all) works like a charm, but when it comes to updating the data, it is really slow. That makes me think that I am doing something wrong with updating the data.

I uploaded a video of my problem on YT: https://youtu.be/GEjHqoAElgI

Additionally I created a minimal reproducible example on codesandbox: https://codesandbox.io/s/blissful-goldwasser-zq8je?fontsize=14&hidenavigation=1&theme=dark
It consists of a Chart.vue component, which gets its property "data" updated, when the button "Update Data" is pressed. The button can be toggled multiple times, and it will always take several seconds until the chart is updated.

Tried giving my component with the Chart the data as property, and have a watcher on that data, to update the Chart whenever the data changes. This works, but updating the chart takes forever (3-5 seconds). What is the recommended way of doing something like that?

This is my component:

<template>
  <VueHighcharts
    class="hc"
    :options="options"
    :deepCopyOnUpdate="true"
    :redrawOnUpdate="true"
    :oneToOneUpdate="false"
    :animateOnUpdate="false"
  />
</template>

<script>
import VueHighcharts from "vue3-highcharts";

export default {
  name: "CoinChart",
  components: {
    VueHighcharts,
  },
  props: {
    stats: [Object],
  },
  data() {
    return {
      options: {
        title: {
          text: "Coins",
        },
        series: [
          {
            name: "ETH",
            data: [],
          },
          {
            name: "BTC",
            data: [],
          },
        ],
      },
    };
  },
  watch: {
    stats: {
      immidiate: true, // as pointed out in the commments, this line does not do anything
      handler: function (val) {
        // create data for chart
        const eth = new Array();
        const btc = new Array();
        this.options.series[0].data = [];
        this.options.series[1].data = [];

        for (let i in val) {
          eth.push([val[i].time * 1000, val[i].coinsPerHour]);
          btc.push([val[i].time * 1000, val[i].btcPerHour]);
        }

        // set to chart
        this.options.series[0].data = eth;
        this.options.series[1].data = btc;
      },
    },
  },
};
</script>

This is driving me crazy, spent 4hours today on trying to fix it, with no success. Even thought about switching to Chart.js, but apparently there is no real support for Vue.js 3 either.


Solution

  • I got to the bottom of this.

    Apparently, when you update the series Highcharts is assigning the new values to the existing series, so that it can calculate any animations. In your case, that's quite heavy, it takes 2-3 seconds, which is what you're seeing.

    It's faster to simply throw the chart away (by using a v-if) and render it anew with the new dataset:

    <template>
      <VueHighcharts v-if="showChart" :options="options" />
    </template>
    
    import VueHighcharts from "vue3-highcharts"
    
    export default {
      name: 'Chart',
      components: {
        VueHighcharts
      },
      props: {
        series: {
          type: Array,
          default: () => []
        }
      },
      data: () => ({
        showChart: true
      }),
      computed: {
        options() {
          return {
            //...
            series: this.series
          }
        }
      },
      watch: {
        series: {
          handler() {
            // this destroys the chart
            this.showChart = false
            this.$nextTick(() => {
              // this renders a new one, with the new set of data
              this.showChart = true
            })
          }
        }
      }
    }