11

I created a simple responsive HTML + JS chart with chart.js which worked well. I decided to do it within Vue CLI and so have tried to switch it to vue-chartjs but the same chart always renders about 33% taller than my window and so presents vertical scrollbars (the width is fine). I recreated the problem with a sample trivial graph which I render with:

import {Line} from 'vue-chartjs'
export default {
  extends: Line,
  mounted () {
    this.renderChart(data, options)
  } 
} 

Note the data is trivial and the options are {}.

If I use chart.js in my Vue component, instead of vue-chartjs then it works fine. I.e. I do nothing more than delete the above code from my component and change it to the following then it renders fine, just like my sample HTML + chart.js version.

import Chart from 'chart.js'
function mount(el) {
    return new Chart(
        document.getElementById(el).getContext('2d'), {
        type: 'line',
        data: data,
        options: options,
    })
}

export default {
    template: '<canvas id="chart"></canvas>',
    mounted () {
        self.chart = mount('chart')
    }
}

I am using the default responsive: true and maintainAspectRatio: false of course, and have no explicit CSS or size settings anywhere.

Why can I not get the chart to render the height correctly when I use vue-chartjs? I am using vue-chartjs version 3.4.2 but have also tried a few versions back. I have looked all over the github bug tracker but seen nothing related.

bulletmark
  • 229
  • 1
  • 2
  • 6

3 Answers3

22

UPDATE:

You should pass the options as prop or locally. But it's needed to add:

  • responsive: true
  • maintainAspectRatio: false

the desired height as well as the options as you said. Here's how it worked for me:

options:

options: {
                    scales: {
                        yAxes: [
                            {
                                ticks: {
                                    beginAtZero: true
                                }
                            }]
                    },
                    responsive: true,
                    maintainAspectRatio: false
                }

In template:

<bin-graph-weight :chart-data="datacollection" :styles="myStyles" :options="datacollection.options"/>

graph-component.js:

import { Line, mixins } from 'vue-chartjs'

const { reactiveProp } = mixins

export default {
    extends: Line,
    mixins: [reactiveProp],
    props: ['options'],
    mounted () {
    // this.chartData is created in the mixin.
        this.renderChart(this.chartData, this.options)
    },
    // If you want to pass options please create a local options object
    watch: {
        chartData () {
            this.$data._chart.update()
        }
    }
}

Despertaweb
  • 1,672
  • 21
  • 29
  • 3
    This helped me: `responsive: true,` and `maintainAspectRatio: false` in the passed options fixed it along with the passed `:styles` – Adsy2010 Jun 30 '20 at 12:47
  • 1
    Yep! :styles is needed. This is documented on the code above! Make sure to set :styles. – Despertaweb Nov 11 '20 at 14:55
7

Also had problem with height overflow and responsiveness, fixed by introducing flex parent container that takes up 100% of the space. After setting responsive and ratio options (check out related chartjs doc):

options: {
  // ..
  responsive: true,
  maintainAspectRatio: true
}

I used following css to fill 100% of the parent (where TheChart is vue-chartjs component. Basically need to make sure the chart's parent is always filling 100% of it's own parent):

vue template

<v-container class="chart-container">
  <TheChart :chartdata="chartData" :options="chartOptions" />
</v-container>

scss:

.chart-container {
  flex-grow: 1;
  min-height: 0;

  > div {
    position: relative;
    height: 100%;
  }
}
Be Kind
  • 4,712
  • 1
  • 38
  • 45
0

With responsiveness the chart rerenders with promises and actually sets two times. With a watcher in Vue.js you can rerender every time with changes in the chartData.

Chart component:

<script>
import { Bar, mixins } from 'vue-chartjs';

const { reactiveProp } = mixins;

export default {
  extends: Bar,
  mixins: [reactiveProp],
  props: ['chartOptions'],
  mounted() {
    this.renderChart(this.chartData, this.chartOptions);
  },
  watch: {
    chartData() {
      this.renderChart(this.chartData, this.chartOptions);
    },
  },
};
</script>

Use together with dynamic styles.

Chart properties:

<template>
  <div style="height:300px;">
    <bar-chart :styles="myStyles" :chart-data="dataCollection"
      :chart-options="chartOptions"></bar-chart>
  </div>
</template>

<script>
import BarChart from './ChartBar.vue';

export default {
  components: {
    BarChart,
  },
  props: ['dataCollection'],
  data() {
    return {
      myStyles: {
        height: '300px',
        width: '100%',
        position: 'relative',
      },
      chartOptions: {
        scales: {
          yAxes: [{
            ticks: {
              beginAtZero: true,
            },
            gridLines: {
              display: true,
            },
          }],
          xAxes: [{
            ticks: {
              beginAtZero: true,
            },
            gridLines: {
              display: false,
            },
          }],
        },
        legend: {
          display: true,
        },
        tooltips: {
          enabled: true,
          mode: 'single',
          callbacks: {
            label(tooltipItems, data) {
              const { datasetIndex, index } = tooltipItems;
              const value = data.datasets[datasetIndex].data[index];
              if (parseInt(value, 10) > 999) {
                return ` ${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`;
              }
              return ` ${value}`;
            },
          },
        },
        responsive: true,
        maintainAspectRatio: false,
        height: 300,
      },
    };
  },
};
</script>

<style lang="scss" scoped>

</style>