I am currently learning VueJs and fiddling around with Chart.js (https://github.com/apertureless/vue-chartjs). I tried to make a doughnut have reactive behaviour, but I only got it to work using the ref property and from my understanding, that's bad style. My first question is, whether the assumption that avoiding $refs is good style is true.
The first problem on my approach was that I have no idea about mixins, but the only example on how to use vue-chartjs reactively used it (https://github.com/apertureless/vue-chartjs/blob/master/src/examples/ReactiveExample.js is the reference point) I created a method in my Vue component called updateData which will reset my components chartData and then set it to the prop data. First of all, this is my code:
chart.blade.php (web view):
<html>
<head>
<meta charset="utf-8">
<title>Testchart</title>
<link rel="stylesheet" href="css/app.css">
</head>
<body>
<div id="app">
<h1>Testchart</h1>
<doughnut :data="doughnut_data" :options="doughnut_options" ref="chart"></doughnut>
<button-reduce v-on:numberreduced="reduce"></button-reduce>
</div>
<script src="js/app.js" charset="utf-8"></script>
</body>
</html>
app.js:
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
Vue.component('doughnut', require('./components/testDoughnut.vue'));
Vue.component('button-reduce', require('./components/button.vue'));
const app = new Vue({
el: '#app',
data: {
doughnut_data: {
labels: ['VueJs', 'EmberJs', 'ReactJs', 'AngularJs'],
datasets: [
{
backgroundColor: [
'#41B883',
'#E46651',
'#00D8FF',
'#DD1B16'
],
data: [40, 20, 80, 10]
}
]
},
doughnut_options: {
responsive: true,
maintainAspectRatio: false
}
},
methods: {
reduce() {
this.doughnut_data.datasets[0].data[2] = this.doughnut_data.datasets[0].data[2] - 5;
this.$refs.chart.updateData();
}
}
});
last but not least, my Vue component testDoughnut.vue
<script>
import { Doughnut, mixins } from 'vue-chartjs'
export default Doughnut.extend({
mixins: [mixins.reactiveData],
props: ["data", "options"],
data() {
return {
chartData: ''
}
},
created() {
this.updateData();
},
mounted () {
this.renderChart(this.chartData, this.options)
},
methods: {
updateData() {
this.chartData = {}; // without this step, it does not work (no reactive behaviour). Why is this necessary?
this.chartData = this.data;
}
}
})
</script>
The following questions arose:
:chartData="doughnut_data"
did not work, I needed to used a custom prop 'data'this.chartData = this.data
without the need of an empty object.vue-chartjs author here.
About the mixins: There are two mixins included. Mixins in vue simply extract some logic and functionality into seperate files, so you can reuse them.
Like in the docs said, there are two mixins.
Because, there are two main scenarios, how you pass data to the chart component. Possibility one is for example in a laravel environment where you pass your data over the props directly to your component.
<my-chart :chart-data="..."></my-chart>
The other usecase is, if you have an API and make a fetch / API request. But then your chart data is not a prop, is a variable in the data() function of vue.
Well you overcomplicate your code a bit.
You rather need the reactiveProp mixin.
<script>
import { Doughnut, mixins } from 'vue-chartjs'
export default Doughnut.extend({
mixins: [mixins.reactiveProp],
props: ["options"],
mounted () {
this.renderChart(this.chartData, this.options)
}
})
</script>
The mixin will create a prop named chartData and add a watcher to it. Every time the data changes, it will either update the chart or re-render. If you add new datasets the chart needs to be re-rendered.