Vue-chartjs: Responsive Width, Fixed Height

Created on 24 Aug 2017  ยท  10Comments  ยท  Source: apertureless/vue-chartjs

Expected Behavior

I would like to render a chart width a 100% width, but a fixed height (e.g. 500px). I would also like the chart to resize according to those constraints (without maintaining aspect ratio).

Actual Behavior

Currently, I'm having trouble working with the responsive/maintainAspectRatio options. Regardless of my styling & options, the height seems to grow beyond it's containing elements (I believe it's using a 1:1 aspect ratio).

I can easily get something working when I feed the chart numeric width/height as a landscape ratio w:h (e.g. <line-chart :chart-data="chartData" :width="5" :height="1" />). Since the prop validation expects a number, I'm wondering if my desired behavior is possible with the current api.

image
image

Environment

  • vue.js version: 2.4.2
  • vue-chart.js version: 2.8.2
  • npm version: 5.3.0
โ“ question

Most helpful comment

Fixed

Switching my component definition to the following solved my problem. This makes me think that somewhere in the chain, my maintainAspectRatio setting was getting merged with the default true in a way that merged the truthy value. Maybe, maybe not. But this works nicely now, so feel free to close this or keep it open for @wannymiarelli .

<script>
import { Line, mixins } from "vue-chartjs"
import { plainObj } from "@/core/object"

export default Line.extend({
  mixins: [mixins.reactiveProp],
  props: {
    options: {
      type: Object,
      default: plainObj
    }
  },
  mounted() {
    this.renderChart(this.chartData, this.options)
  },
  data() {
    return {
      defaultOptions: {
        responsive: true, // my new default options
        maintainAspectRatio: false, // my new default options
        scales: {
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true // my new default options
            }
          }],
          xAxes: [{
            gridLines: {
              display: false
            }
          }]
        }
      },
      plugins: []
    }
  },
})
</script>

All 10 comments

With responsive: true the canvas will ignore the width and height parameters and grow always 100% of the outer container.

With the @2.8 release you can pass two additional props.
One is the css-classes for the outer div and the other is a styles object which gets applied as inline styles to the outer div.

https://github.com/apertureless/vue-chartjs/releases/tag/v2.8.0

So I would not go over the width and height props, because they are applied directly to the cnavas. You can try to either pass a style object with

{
    height: '500px',
    width: '100%',
    position: 'relative'
}

Or you can make a css class with that properties and pass it.

Using the styles as follow
<sale-chart :styles="{height: '250px', position: 'relative'}"></sale-chart>
the chart does not respect the given size of 250px

Do you have maintainAspectRatio set to true or false?

Can you maybe post a minimal codepen for reproduction?

Here's a fiddle: https://jsfiddle.net/alexsasharegan/v6dj7qr3/

It appears to be working in here. I'll double back on my setup and see what I spot that's off.

Update

The difference I can see is that somehow the height setting is not propagating down to the canvas styles. I have everything set up as identically as possible. I'm reinstalling the lib and inspecting my webpack config for any possible hangups.

My App

image

My fiddle

image

Interesting... in your Line Chart render function, I don't see the code that would propagate the style object down on the canvas like I'm seeing in the fiddle repro:

export default Vue.extend({
  render: function (createElement) {
    return createElement(
      'div', {
        style: this.styles,
        class: this.cssClasses
      },
      [
        createElement(
          'canvas', {
            attrs: {
              id: this.chartId,
              width: this.width,
              height: this.height
            },
            // Wouldn't there be a copy of the style obj here?
            ref: 'canvas'
          }
        )
      ]
    )
  },
}

Any idea how the fiddle has the identical style settings as the wrapper?

Fixed

Switching my component definition to the following solved my problem. This makes me think that somewhere in the chain, my maintainAspectRatio setting was getting merged with the default true in a way that merged the truthy value. Maybe, maybe not. But this works nicely now, so feel free to close this or keep it open for @wannymiarelli .

<script>
import { Line, mixins } from "vue-chartjs"
import { plainObj } from "@/core/object"

export default Line.extend({
  mixins: [mixins.reactiveProp],
  props: {
    options: {
      type: Object,
      default: plainObj
    }
  },
  mounted() {
    this.renderChart(this.chartData, this.options)
  },
  data() {
    return {
      defaultOptions: {
        responsive: true, // my new default options
        maintainAspectRatio: false, // my new default options
        scales: {
          yAxes: [{
            ticks: {
              beginAtZero: true
            },
            gridLines: {
              display: true // my new default options
            }
          }],
          xAxes: [{
            gridLines: {
              display: false
            }
          }]
        }
      },
      plugins: []
    }
  },
})
</script>

Perhaps having : maintainAspectRatio: trueas default would be a good way to go.

@DespertaWeb

As this issue is quite old (nearly 2 years), there are no default options anymore.
With vue-chartjs v3 all stylings and options made by me were removed. The reason is that you now have a clean chart component, as you would use chart.js directly.

The height rerenders with promises.

<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>

The watcher rerenders the chart with changes in the chartData. Use together with dynamic styles ->

<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 `\u20AC ${value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`;
              }
              return `\u20AC ${value}`;
            },
          },
        },
        responsive: true,
        maintainAspectRatio: false,
        height: 300,
      },
    };
  },
};
</script>

<style lang="scss" scoped>

</style>

I had the same problem and fixed it by adding the renderChart call in a chartData watcher... However, isn't it kinda weird having to use this solution while using the reactiveProp mixin? Shouldn't the mixin already do all the work by itself?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aido179 picture aido179  ยท  3Comments

rzb picture rzb  ยท  4Comments

humanismusic picture humanismusic  ยท  3Comments

DavidSotoA picture DavidSotoA  ยท  3Comments

timster picture timster  ยท  5Comments