Closed cdubant closed 7 years ago
Well, you don't need to use Vue's $nextTick()
function, because vue does not render the chart.
The chart gets renderes by chart.js on a canvas.
You can simply make your api call in the mounted()
hook.
However your response.data
need to have a valid chart.js datatructure.
But keep in mind the limitations to the watcher. There are also some issues related to this: #44 #34
Hi @cdubant
I recently had to implement something like this and I was kinda stuck. Here's what I did (with help from Vuex);
First thing I did was store what I needed from the API call in Vuex so I could pretty much access it anywhere. Then I wrote a computed
object that returns the the data in Vuex and uses it to build the chart; something like this
datasetsfull () {
return {
labels: store.state.chartdays,
datasets: [
{
label: 'Time Signed In',
backgroundColor: '#40CF97',
data: store.state.charttimes
}
]
}
},
chartdays () {
return store.state.chartdays
},
charttimes () {
return store.state.charttimes
}
}
And then in my template
I simply used the datasetsfull
function; something like this;
<reports-charts :chart-data="datasetsfull" :height="400" :options="{responsive: true, maintainAspectRatio: false}"></reports-charts>
I hope that was useful to you 😄
@apertureless the thing is that even if I do the following
LineChart.vue
<template>
<div>
<line-chart :chart-data="datacollection"></line-chart>
</div>
</template>
<script>
import LineChart from './LineChart.js'
export default {
components: {
LineChart
},
data () {
return {
datacollection: null
}
},
mounted () {
this.fillData;
},
methods: {
fillData () {
this.$http.get('/api/data/line/scenarios_by_day')
.then(response => {
this.datacollection = response.data;
});
},
}
}
</script>
LineChart.js
import { Line, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default Line.extend({
mixins: [reactiveProp],
props: ['options'],
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
})
The fillData method never gets triggered, I never see the API call.
When I add a button to trigger this method, then the API call is made and the data is displayed just fine. My response.data is valid JSON (expected format) built by my Laravel Spark backend.
The thing is that I can't have my users perform any action before displaying the charts, it would make no sense on an ergonomic POV.
@yomete Thanks for your insight but I'm afraid I'm not familiar with Vuex so it might be a little overkill to implement / configure Vuex for my (I hope) "simple" requirement. Anyway thanks again for providing a solution.
Well, your fillData()
is a method. You should call it like one.
mounted () {
this.fillData();
},
Besides: I don't really understand why you have two LineChart components? Also with the exact same name. Is there a reason behind this?
@apertureless you're right, though it doesn't help in rendering the chart.
After fixing the method call, I saw the API call but it didn't help in rendering the graph.
I created 2 different files because of http://vue-chartjs.org/#/home?id=reactive-data (I can see a js file and something that looks like a vue file, that's why I created both).
I found a simpler solution by having a single component and doing the following (as simple as that)
import { Line } from 'vue-chartjs'
export default Line.extend({
data () {
return {
resultSet: null
}
},
props: ['options'],
mounted () {
this.fillData();
},
methods: {
fillData () {
this.$http.get('/api/data/line/scenarios_by_day')
.then(response => {
this.resultSet = response.data;
this.renderChart(this.resultSet, this.options)
});
},
}
})
It now works as expected, thanks for your time. :+1:
Have the same problem - chart is not redrawn after datasets change.
Can you prodive a jsfiddle / codepen ? For reproduction? It heavily depends on how you populate your data, if you're using the reactive prop etc.
@apertureless I can provide code here because I use API requests. My vue component looks like this:
<template>
<div>
<pie-chart :chart-data="pieChartData"></pie-chart>
</div>
</template>
<script>
import axios from 'axios'
import PieChart from './ui/PieChart.js'
export default {
components: {
PieChart
},
data () {
return {
pieChartData: {
labels: ['Android', 'iOS'],
datasets: [{
backgroundColor: [
'#a4c639',
'#5fc9f8'
],
hoverBackgroundColor: [
'#a4c639',
'#5fc9f8'
],
data: null
}]
},
mounted () {
this.getDevices()
},
methods: {
getDevices: function () {
Promise.all([
axios.get(`/devices?platform=android`),
axios.get(`/devices?platform=ios`)
])
.then(responses => {
this.pieChartData.datasets[0].data = responses.map(function (response) {
return response.data.total
})
})
}
}
And PieChart.js:
import { Pie, mixins } from 'vue-chartjs'
const { reactiveProp } = mixins
export default Pie.extend({
mixins: [reactiveProp],
props: ['chartData', 'options'],
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
})
You could try the solution mentioned here https://github.com/apertureless/vue-chartjs/issues/10#issuecomment-289494398
The problem is like mentioned in #44 the limitations
Note: when mutating (rather than replacing) an Object or an Array, the old value will be the same as new value because they reference the same Object/Array. Vue doesn’t keep a copy of the pre-mutate value.
If it's not working you could try an event based system, where you trigger an event and listen on it and then call update() yourself.
@apertureless anyway can't make it work. Vue Devtools shows me that the data is successfully changed, but the pie chart is still empty:
If I click on the label, the chart is redrawn, but not automatically.
reactiveProp instructions didn't help me too.
Have you tried the solution mention in #10 ? Without the reactive prop mixin, and add a own watcher?
Like I said, not really vue-chartjs
related. Rather how you handle data.
Sure the data is successfully changed, but like mentioned in #44 vue does not keep the old value in the watcher. So it does not recognize that there is a state change. Therefore it the watcher does not trigger.
@apertureless yes, I changed my code like this:
import { Pie } from 'vue-chartjs'
export default Pie.extend({
props: ['data'],
mounted () {
this.renderChart(this.data)
},
watch: {
data: function () {
this._chart.destroy()
this.renderChart(this.data)
}
}
})
But in my case it doesn't help me, don't know why
@moonlik Have you solved? I have the same problem like you. If I click on the label twice, the chart will redrawn.
@dwk715 Not yet, I have no idea how to solve it for now
Well you could fire an event if new data arrives and then re-render or update the chart instance.
Expected Behavior
In the example for Reactive Data in the docs, when implemented, you have to click the "Randomize" button to trigger the fillData function and get content displayed in the graph.
Actual Behavior
I'm looking for a way (as I serve the content to display from an API) to display the data as soon as the component gets a response from my API.
I tried to do the usual Vue 2.0 nextTick to call the fillData function when the component is mounted
Below is the code of my component (slightly adapted to match my API call), of course it works perfectly when I click the "Randomize" button but I don't want to have to click this button for each and every graph in my "Dashboard" component (several graphs in this component).
Thank you in advance for your help !
Environment