Implementing chart.js with vue has been successful so far, but I encountered an issue when fetching chart data via axios. The chart does not display anything because the data is not available when the chart component is mounted. I am looking for a solution to dynamically rerender the charts once the data in my component is loaded instead of waiting for the Ajax call to finish...
I pass all the necessary data for my chart through props to the chart component:
<template>
<section class="CHARTmaincontainer">
<canvas :id="id" :width="width" :height="height"></canvas>
</section>
</template>
<!--SCRIPTS-->
<script>
import Chart from 'chart.js';
export default {
name: 'ChartJS',
props:
{
id:{ required:true, type:String },
type:{ default:'bar', type:String },
width:{ default:400, type:Number},
height:{ default:175, type:Number },
data:{ required:true, type:Array },
label:{ default:'Gráfico', type:String },
labels:{ required: true, type:Array }
},
mounted()
{
let ctx = document.getElementById(this.id);
let chart = new Chart(ctx, {
type: this.type,
data: {
labels: this.labels,
datasets: [{
label: this.label,
data: this.data,
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: true
}
}],
xAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: true
}
}]
},
},
legend: {
display: false
},
tooltips: {
enabled: true,
mode: 'single',
callbacks: {
label: function(tooltipItems, data) {
return '$' + tooltipItems.yLabel;
}
}
},
responsive: true,
maintainAspectRatio: false,
});
},
};
</script>
<!--STYLES-->
<style scoped>
.CHARTmaincontainer{width:100%; display:flex; flex-direction:column; height:auto; margin:20px 0px;}
</style>
Below is the component where I include my chart components and provide the data:
<template>
<section class="entry_maincontainer">
<chart-js v-if="ordersCount"
:id="'grafico1'"
:data="ordersCount"
:label="'Descuentos vendidos'"
:labels="['Ene', 'Feb', 'Mar', 'Abr', 'May', 'Jun', 'Jul', 'Ago', 'Sep', 'Ago', 'Oct', 'Nov', 'Dic']">
</chart-js>
</section>
</template>
<!--SCRIPTS-->
<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';
export default {
name: 'StatsPanel',
computed:
{
...mapState('Orders', ['orders']),
...mapGetters('Orders', ['ordersCount', 'ordersTotal']),
...mapState('Globals', ['globals']),
...mapState('Loader', ['loader']),
},
mounted()
{
console.log(this.$options.name+' component successfully mounted');
this.getAll();
},
methods:
{
...mapActions('Orders', ['getAll']),
...mapMutations('Loader', ['RESET_LOADER']),
}
};
</script>
<!--STYLES-->
<style scoped>
</style>
The main data prop used to render the chart is the following getter:
ordersCount: state => {
let monthlyCount = { Enero:0, Febrero:0, Marzo:0, Abril:0, Mayo:0, Junio:0, Julio:0, Agosto:0, Septiembre:0, Octubre:0, Noviembre:0, Diciembre:0 };
_.forEach(state.orders, function(order) {
let capitalizedMonth = _.upperFirst(Vue.moment(order.created_at).format('MMMM'));
monthlyCount[capitalizedMonth] = parseInt( monthlyCount[capitalizedMonth] ) + parseInt( order.discountcodes.length );
});
let values = Object.values(monthlyCount);
return values;
},