Chartjs-Flickering of charts and getcontext error with chartjs in the context of Vuejs

1๐Ÿ‘

โœ…

As I wrote in my comment, the charts are rendered twice. This causes flickering.

// every time you use <chart-display>, 2 charts are rendered, this means chart 1 renders
// itself and chart 2, char 2 renders itself and chart 1, this is a bad pattern in Vue in general

mounted() {
  this.check(8, "chart_8");
  this.check(7, "chart_7");
}

Make the following changes:

ChartDisplay.vue

<template>
  <div
    class="chart-container"
    style="position: relative; height: 40vh; width: 100%"
  >
    <canvas ref="chart_7"></canvas>
    <canvas ref="chart_8"></canvas>
  </div>
</template>

<script>
import Chart from "chart.js";
export default {
  name: "ChartDisplay",
  data() {
    return {
      date: [],
      challenge: [],
      data: [],
      // save charts in an array
      charts: [],
      // charts options
      options: {
        lineTension: 0,
        maintainAspectRatio: false,
        scales: {
          yAxes: [
            {
              scaleLabel: {
                display: false,
              },
              ticks: {
                beginAtZero: true,
                callback(value) {
                  return `${value}%`;
                },
              },
            },
          ],
          xAxes: [
            {
              type: "time",
              time: {
                unit: "month",
              },
              scaleLabel: {
                display: true,
              },
            },
          ],
        },
      },
    };
  },
  mounted() {
    this.render(7, this.$refs.chart_7);
    this.render(8, this.$refs.chart_8);
  },
  methods: {
    render(id, ctx) {
      this.fetchData(id).then((response) => {
        let data = response.date.map((date, index) => ({
          x: new Date(date * 1000),
          y: response.challenge[index],
        }));
        this.charts.push(
          new Chart(ctx, {
            type: "line",
            data: {
              datasets: [
                {
                  label: "Challenge",
                  data: data,
                  borderColor: " #EA5455",
                },
              ],
            },
            options: this.options,
          })
        );
      });
    },
    fetchData(id) {
      return this.$http.get(`/api_chart/${ id  }/full`);
    },
  },
  beforeDestroy() {
    this.charts.forEach((chart) => chart.destroy());
  },
};
</script>
<style >
[v-cloak] {
  display: none;
}
</style>

App.vue

<template>
  <div>
    <div class="In order to display chart1">
      <chart-display/>
    </div>
  </div>
</template>

<script>
import ChartDisplay from "./ChartDisplay.vue";
export default {
  components: { ChartDisplay },
};
</script>

See it on sandbox

1๐Ÿ‘

I found several errors on your code. I fix them in Sandbox

For Chat.vue :

  • I rename the file as ChartDisplay.vue as similar as the component name
  • import chart.js package for using Chart() function
  • I use a demo API
<template>
      <div
        class="chart-container"
        style="position: relative; height: 40vh; width: 100%"
      >
        <slot name="test1"></slot>
        <slot name="test2"></slot>
      </div>
    </template>

<script>
  import Chart from "chart.js";
  export default {
    name: "ChartDisplay",
    data() {
      return {
        date: [],
        challenge: [],
        data: [],
      };
    },
    mounted() {
      this.check(8, "chart_8");
      this.check(7, "chart_7");
    },
    methods: {
      check(id, name) {
        fetch(
            "https://api.wirespec.dev/wirespec/stackoverflow/fetchchartdataforvuejs"
          )
          .then((response) => response.json())
          .then((response) => {
            this.date = response.date;
            this.challenge = response.challenge;
            this.data = this.date.map((date, index) => ({
              x: new Date(date * 1000),
              y: this.challenge[index],
            }));
            const ctx = document.getElementById([name]).getContext("2d");
            new Chart(ctx, {
              type: "line",
              data: {
                datasets: [{
                  label: "Challenge",
                  data: this.data,
                  borderColor: " #EA5455",
                }, ],
              },
              options: {
                lineTension: 0,
                maintainAspectRatio: false,
                scales: {
                  yAxes: [{
                    scaleLabel: {
                      display: false,
                    },
                    ticks: {
                      beginAtZero: true,
                      callback(value) {
                        return `${value}%`;
                      },
                    },
                  }, ],
                  xAxes: [{
                    type: "time",
                    time: {
                      unit: "month",
                    },
                    scaleLabel: {
                      display: true,
                    },
                  }, ],
                },
              },
            });
          });
      },
    },
  };
</script>

For App.vue

  • Your import should not carry any hyphen.
  • component should be components
  • render the component once to avoid flikering
<template>
  <div>
    <div class="In order to display chart1">
      <chart-display>
        <canvas slot="test1" id="chart_7"></canvas>
        <canvas slot="test2" id="chart_8"></canvas>
      </chart-display>
    </div>
  </div>
</template>

<script>
  import ChartDisplay from "./ChartDisplay.vue";
  export default {
    components: {
      ChartDisplay
    },
  };
</script>

Leave a comment