3
votes

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:

  1. (from above): Is avoiding $refs a good thing to do?
  2. Why is it impossible to update chartData directly from my webview? :chartData="doughnut_data" did not work, I needed to used a custom prop 'data'
  3. In my testDoughnut.vue, it is necessary to first reset chartData to an empty JSON object before assigning it to this.data. Why is this reset necessary? From desktop development (C#), I thought I could just write this.chartData = this.data without the need of an empty object.
  4. Is there any better way to deal with this problem instead of the way I did it (using ref)?
1

1 Answers

3
votes

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.

  • reactiveProp and
  • reactiveData

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.

Fix

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.

Your Questions

  1. Well you don't need $ref, if you use the right mixin
  2. camelCase in templates needs to be written with a dash '-'
  3. Maybe it is buggy, because you duplicated the data attribute. The mixins always automatically create your chartData as a property or data() function variable. So you don't need to create it by your own. However I guess it could be a race condition. Where data is not set in the create() hook of vue. https://vuejs.org/v2/guide/instance.html#Lifecycle-Diagram