Skip to content
Advertisement

Charts.js with vue not drawing charts after fetching data

This is how json response looks like [[61,57,34],[1,1,3]] I want to use the first array for labels and the second for data.

If I set labels and data inside app manually it works though.

eg labels: ["q", "w", "e"] data: [1, 5, 10]

Vue.component('chart', {
  props: ['labels', 'data', 'type'],
  template: `
    <canvas style="width: 512px; height: 256px"></canvas>
  `,
  mounted: function () {
    new Chart(this.$el, {
      type: this.type,
      data: {
          labels: this.labels,
          datasets: [{
              label: '# of Votes',
              data: this.data,
              borderWidth: 1
          }]
      },
      options: {
          scales: {
              yAxes: [{
                  ticks: {
                      beginAtZero:true
                  }
              }]
          }
        }
      })
  }
});

new Vue({
  el: '#app',
  data: {
    message: "test",
    labels: [],
    data: []
  },
  methods: {
    fetchData: function() {
      this.$http.get('/admin/fetch_data').then(res => {
        this.labels = res.body[0];
        this.data = res.body[1];
      })
    }
  },
  beforeMount() {
    this.fetchData()
  }
});

component on page

<div id="app">
  {{message}}
  <chart :labels="labels" :data="data" type="bar"></chart>
</div>

The data seems to be loaded but there’re no chart bars on page.

enter image description here

Advertisement

Answer

The problem is your data is not yet fetched as you are performing an async task to fetch your data. By that time the component’s mounted hook is called with empty props as the data you are passing as props is not yet loaded.

So do it like this:

Vue.component('chart', {
  props: ['labels', 'data', 'type' 'loaded'],
  template: `
    <canvas style="width: 512px; height: 256px"></canvas>
  `,
  watch:{
      loaded(isLoaded){
          if(isLoaded){
              this.drawChart();
          }
      }
  },
  methods:{
      drawChart: function () {
        new Chart(this.$el, {
          type: this.type,
          data: {
              labels: this.labels,
              datasets: [{
                  label: '# of Votes',
                  data: this.data,
                  borderWidth: 1
              }]
          },
          options: {
              scales: {
                  yAxes: [{
                      ticks: {
                          beginAtZero:true
                      }
                  }]
              }
            }
          }) 
    }
  }
});


new Vue({
  el: '#app',
  data: {
    message: "test",
    labels: [],
    data: [],
    loaded: false
  },
  methods: {
    fetchData: function() {
      this.$http.get('/admin/fetch_data').then(res => {
        this.labels = res.body[0];
        this.data = res.body[1];
        this.loaded = true;
      })
    }
  },
  created() {
    this.fetchData()
  }
});

html

<div id="app">
  {{message}}
  <chart :labels="labels" :data="data" :loaded="loaded" type="bar"></chart>
</div> 
  • add a property loaded set to false in your root vue instance and pass it as a prop

  • change the loaded to true in the success callback of the promise returned by your ths.$http.get() request

  • setup a watcher in your chart component watching over this loaded prop

  • invoke the drawChart method when the loaded propchanges totrue`

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement