Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update() not refreshing chart #148

Closed
Blaapje opened this issue Jul 17, 2017 · 5 comments
Closed

update() not refreshing chart #148

Blaapje opened this issue Jul 17, 2017 · 5 comments

Comments

@Blaapje
Copy link

Blaapje commented Jul 17, 2017

I fetch my data asynchronously, fetching 100 JSON objects separately. I save these objects in chartData.datasets. In case it matters, all JSON objects are time series.

In the actual line-chart component I have declared a $watch that listens for change. This function works correctly and reports overtime a new dataset has been added to chartData. However, calling the update() function doesn't do anything.

Only when I wait until all JSON objects are loaded, and I call destroy() and render a new chart, the data wil appear.

Here's my line component:

<script>
import VueCharts from 'vue-chartjs'

export default VueCharts.Line.extend({
  mixins: [VueCharts.mixins.reactiveProp],
  props: ['chartData', 'options'],
  mounted () {
    this.renderChart(this.chartData, this.options)
  },
  watch: {
    chartData: {
      handler: function (val) {
        if (this.chartData.datasets.length === 100) {
          this._chart.destroy()
          this.renderChart(this.chartData, this.options)
        }
      },
      deep: true
    }
  }
})
</script>

The view implementing this component is rather obnoxious as I've been messing with it for a few hours now, would rather not share. I think most important here is that I update the datasets with this.chartData.datasets.append(new_dataset).

Expected Behavior

Fetched datasets from asynchronous get calls should be plotted once they are added as a new dataset.

Actual Behavior

Fetched datasets from asynchronous get calls are successfully noticed by the watcher, but calling this._plot.update() does not show them in the plot.

Additional information

In some settings I encountered maximum call stack size limit exceeded errors. I would briefly see the first plot after which the error would be thrown and the chart would crash. This has me leading to believe I might be using the wrong data structures for this.

Environment

  • vue.js version: ^2.6.0
  • vue-chart.js version: ^2.7.1
  • yarn version: 0.27.5
@apertureless
Copy link
Owner

Hey @Blaapje

well it is a bit confusing, what you are doing.

  1. You are using the reactiveProp mixin, but you are adding an own watcher.
    So your own watcher will AFAIK overwrite the mixin.

  2. I would remove the deep: true in your mixin, because the object will be way to big with all the datasets to compare deep.

  3. What is this.chartData.datasets.append(new_dataset) ? Or rather, what is append() ? It's not a native array function o__O

  4. What should this._plot.update() do ? oO what is this._plot in your case?

You can call this._chart.update() which is a chart.js function to update the chart without completeley destroying and rerendering it. (no flashing) But you should do it in your chart component watcher. Or use the mixin ^, ^

@Blaapje
Copy link
Author

Blaapje commented Jul 18, 2017

Hey @apertureless,

Thanks for your reply. I mixed up some names writing down this Issue:append() should be push() and this._plot.update() should be this._chart.update() as you already pointed out.

I've tried removing both the reactiveProp and my watcher separately, but that didn't change anything in the result. Removing the deep: true property (or setting it to false) causes the watcher to not see any updates at all.

I've made a codepen where I replicated my local setup as best as possible: CodePen.io: GitHub Issue 148.

I've created a hardcoded destroy and renderChart() on lines 20-11, which will render the data. But update() doesn't work in this example.

@Blaapje
Copy link
Author

Blaapje commented Jul 21, 2017

I just got this working and thought I'd share: https://codepen.io/anon/pen/vZwaGg

I'm using the ref attribute to update the child component from the parent directly. The implementation given is probably not perfect, but it works and can be adjusted if needed.

I'd like to reference some other issues #78 and #10 that were dealing with this and to my likings never included a satisfying answer with example for new Vue users.

For the cause of the failed updated I think that the entire chartData object gets filled with promises all at once and as such doesn't see newly add data, ergo chartJS is unable to handle promises correctly. This is just a wild guess though

@apertureless
Copy link
Owner

Well, there are some drawbacks with reactive data arrays, as vue can't recognize mutations, as they don't hold a reference to the old value. Like written in the docs.

And Chart.js does not provide a watcher / observer for data. There is a workaround in #44 . If you work with $set for example.

However a few points you should keep in mind:

  • If you initially fetching data, you should add a v-if to your chart component. As the fetch is async and your component will be mounted before the data arrives.
  • The reactiveMixins are only really needed if you want something like a real time data visualisation. If you only fetch from time to time, you can simply re-render the chart. You can for example dispatch events or working with props to check if you're fetching new data.

You can also check out this repo https://github.com/apertureless/npm-stats or the resources section in the docs. I've posted some tutorials on working with apis and vue-chartjs .

@phaptq
Copy link

phaptq commented Apr 23, 2018

Work for me:
`<script>
import { Pie } from 'vue-chartjs'

export default {
extends: Pie,
props: ['billings'],
mounted () {

},
watch: {
    billings(){
        if (Object.keys(this.billings).length) {
            var labels = [];
            for (var i = 0; i < Object.keys(this.billings).length; i++) {
                labels.push(Laravel.bill_type[Object.keys(this.billings)[i]]);
            }
            this.renderChart({
                labels: labels,
                datasets: [
                    {
                        backgroundColor: [
                            '#41B883',
                            '#E46651',
                        ],
                        data: Object.values(this.billings)
                    }
                ]
            }, {responsive: true, maintainAspectRatio: false})
        }else{
            this._chart.destroy()
        }
    }
}

}
</script>`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants