Updating Chartjs Data with Response from POST Call? - javascript

I am currently using react-chartjs-2 to be able to insert a chart into a react component. I am importing the data and options of the chart which are located in another js file. In my app, I am also making a POST call which returns some data in it's body which I want to use as the data for the chart and the chart to be able to update every time a POST request is called. The POST response is currently stored in the state called RESTResponse. So to access the response data outside of the chart in react, I normally call {this.state.RESTresponse.total}. I want to be able to use {this.state.RESTresponse.total} as the data in my chart. How would I be able to do this? Would it be easier if I didn't use two separate js files for the chart and main component? Thank you!
Here is the code where the chart is being called:
import {HorizontalBar} from "react-chartjs-2";
import {Row} from "reactstrap";
// Importing the chart data and options to be used in <HorizontalBar>
import {stackedChart} from "variables/charts.js";
class Dashboard extends React.Component {
constructor(props) {
super(props);
this.state = {
RESTresponse: []
};
}
async onTodoChange(event){
let updateJSON = {...this.state.RESTresponse, [event.target.name] : val}
const requestOptions = {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(updateJSON)
};
const response = await fetch('/api/calculate', requestOptions);
const body = await response.json();
this.setState({RESTresponse : body });
}
render() {
return (
<>
<div className="content">
<Row>
<HorizontalBar
data={stackedChart.data}
options={stackedChart.options}
/>
</Row>
</div>
</>
);
}
}
And here is the code where the data and options are defined:
let stackedChart = {
data: canvas => {
return {
datasets: [
{
label: ' Total Value',
data: [10], //<--- I want to dynamically update this data value from the POST response
backgroundColor: '#C4D156'
}
]
};
},
options: {
maintainAspectRatio: false,
legend: {
display: true,
labels: {
usePointStyle: true,
borderWidth: 0,
filter: function(legendItem, chartData) {
if (legendItem.datasetIndex === 3) {
return false;
}
return true;
}
}
},
tooltips: {enabled: false},
hover: {mode: null},
responsive: true,
scales: {
yAxes: [
{stacked: true},
],
xAxes: [{stacked: true,
ticks: {
display: false
}
},
]
}
}
};
module.exports = {
stackedChart
};

When data is a function, is it invoked with node where the chart is mounted. However you don't seem to be having need for it currently.
Declare data to receive Server response data.
dataFactory: data => {
return {
datasets: [
{
label: ' Total Value',
data,
backgroundColor: '#C4D156'
}
]
};
},
Then invoke it in render method of your component with data stored in state.
<HorizontalBar
data={stackedChart.dataFactory(this.state.RESTresponse)}
options={stackedChart.options}
/>

Related

Updating chart with data from Firestore realtime listener

In my Vue app I am using Apexcharts to display some data.
I am grabbing the data from my Firestore database by using a realtime listener.
The chart is working as it should and I am also getting the data in realtime. The problem is that my chart is not updating itself with the new data, and I am not sure on how to approach it.
I am fetching the data my parent component through this script:
onMounted(async () => {
const unsub = onSnapshot(doc(db, "testUsers", "rtBp8UHReBE2rACDBHij"), (doc) => {
getWeight.value = doc.data();
});
watchEffect((onInvalidate) => {
onInvalidate(() => unsub());
});
});
I am sending the data to my child component through props like this:
watch(
() => props.getWeight,
(getWeights) => {
weight.value = [...getWeights.weightData.weight];
let numeric = { day: "numeric", month: "numeric" };
getWeights.weightData.date.forEach((dates) => {
date.value.push([dates.toDate().toLocaleDateString("se-SW", numeric)]);
}),
}
);
My chart in the child component looks something like this:
<apexchart class="apexchart" type="line" :options="options" :series="series">
</apexchart>
<script>
export default {
props: ["weight", "date"],
setup(props) {
return {
options: {
xaxis: {
type: "category",
categories: props.date,
axisBorder: {
show: false,
},
},
},
series: [
{
name: "Værdi",
data: props.weight,
},
],
};
},
};
</script>
How can I make my chart update with the new data from the realtime listener?
If in chart options you add id you would be able to call exec and update your chart
Example:
import ApexCharts from "apexcharts";
ApexCharts.exec('chartId', 'updateOptions', {
series: [
{
name: 'Værdi',
data: newWeights,
},
],
xaxis: {
categories: newDates,
},
})

How to get chart data from REST GET to appear during initialisation for this vue component?

I modified an apexcharts Vue component BarChart.vue which is from https://github.com/apexcharts/vue3-apexcharts
I want to retrieve chart data by consuming a REST GET API and insert data into series.
The script portion of this component is as follows;
<script>
/* eslint-disable */
export default {
name: "BarExample",
data: dataInitialisation,
methods: {
updateChart,
},
};
async function makeGetRequest(url) {
const axios = require("axios");
//let res = await axios.get("http://localhost:8080/vue3-apexcharts/data.json");
let res = await axios.get(url);
return res.data;
}
function dataInitialisation() {
let init_data = {
chartOptions: {
chart: {
type: "bar",
stacked: true,
animations: {
enabled: true, //disabling will speed up loading
},
},
},
series: {},
};
var url = "http://localhost:8080/vue3-apexcharts/data.json";
const axios = require("axios");
var data;
makeGetRequest(url).then(
(data) =>
{
console.log(JSON.stringify(data));
init_data.series = data;
}
);
return init_data;
}
I verified that there is nothing wrong with the code for getting the data from REST GET by printing out the data using console.log().
I did some research and it seems I need to use mounted() to get the data to appear on the chart. If this is correct, how do I modify the code to use mounted() to do so?
I am using vue 3.
Couple of things.
Never define functions and logic outside the Vue api inside a Vue component.
What's defined in data, should be defined in data every doc that you will encounter does that the same way. Data Properties and Methods.
Answering your question yes, you need a lifecycle hook for fetching the data from the api when the component mounts, you can read more about lifecycle hooks in this article
// From this line until the end just delete everything.
// Define `methods` and `data` where they belong.
function dataInitialisation() {
let init_data = {
chartOptions: {
Here is a refactored example:
<script>
import axios from 'axios'
export default {
name: 'BarExample',
data() {
return {
url: 'http://localhost:8080/vue3-apexcharts/data.json',
chartOptions: {
chart: {
type: 'bar',
stacked: true,
animations: {
enabled: true
}
}
},
series: {}
}
},
async mounted() {
await this.makeGetRequest()
},
methods: {
updateChart, // Where is this method defined?
async makeGetRequest() {
const { data } = await axios.get(this.url)
this.series = data
}
}
}
</script>
I will answer my own question. The key to the answer comes from the mounted event available in the vue-apexcharts library.
https://apexcharts.com/docs/options/chart/events/
I used this article here as a guide on how to use events in vue3-apexcharts.
https://kim-jangwook.medium.com/how-to-use-events-on-vue3-apexcharts-2d76928f4abc
<template>
<div class="example">
<apexchart
width="500"
height="350"
type="bar"
:options="chartOptions"
:series="series"
#mounted="mounted"
></apexchart>
</div>
</template>
<script>
async function makeGetRequest(url) {
const axios = require("axios");
//let res = await axios.get("http://localhost:8080/vue3-apexcharts/data.json");
let res = await axios.get(url);
return res.data;
}
export default {
name: "BarExample",
data: dataInitialisation,
methods: {
updateChart,
mounted: function(event, chartContext, config) {
console.log("mount event");
var url = "http://localhost:8080/vue3-apexcharts/data.json";
const axios = require("axios");
var data;
makeGetRequest(url).then((data) => {
this.series = data;
});
},
},
};
</script>

How to update a chart using VueJS and ChartJS

I'm trying to update a chart using VueJS and ChartJS and so far i can access every property of the object but if i try to change the object's property i get an error :
[Vue warn]: Error in mounted hook: "TypeError: _chart_data_js__WEBPACK_IMPORTED_MODULE_5__.planetChartData.update is not a function"
I went to ChartJS's tutorial section and issues sections but i couldn't find any clue for this problem.
What i find strange is that the 'push' function is working perfectly fine.
So far what i'v try is :
.vue file
<template>
<div id="app" style="position: relative; height:500px; width:500px">
<canvas :width="300" :height="300" id="planet-chart"></canvas>
</div>
</template>
...
import { mapActions, mapState } from 'vuex'
import Chart from 'chart.js';
import {planetChartData,pie} from './chart-data.js';
// import { mapActions } from 'vuex'
// import { connectionsAlive } from '../../api/mkt-api.js'
export default {
mounted() {
var x=this.createChart('planet-chart', this.planetChartData)
planetChartData.data.labels.push('Janvier', 'Février')
planetChartData.update();
},
data () {
return {
planetChartData: planetChartData,
}
},
methods: {
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
const myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
}
}
}
</script>
And .js file
export const planetChartData = {
type: 'bar',
data: {
labels: ['Janvier', 'Février', 'Mars', 'Avril'],
datasets: [
{ // one line graph
label: 'Number of users',
data: [3018, 3407, 3109,1060],
backgroundColor: [
'rgba(54,73,93,.5)', // Blue
'rgba(54,73,93,.5)',
'rgba(54,73,93,.5)',
'rgba(54,73,93,.5)'
],
borderColor: [
'#36495d',
'#36495d',
'#36495d',
'#36495d'
],
borderWidth: 3
},
]
},
options: {
responsive: true,
lineTension: 1,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
padding: 40,
}
}]
}
}
}
Maybe i'm using the wrong syntax, if anyone has an idea let me know, thanks.
Regards.
In the vue file, planetChartData is a reference to the object "planetChartData" from your js file. It is not a reference to the chart you create in createChart()
What you want is to return the created chart, so you can call update() on it:
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
const myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
return myChart // <<< this returns the created chart
}
Then in mounted you can do this:
var chart = this.createChart('planet-chart', planetChartData)
chart.update();

Vue.js - this.<value>.<value> is undefined

<div class="content-chart">
<chart :type="'line'" :data="lineData" :options="options"></chart>
</div>
Above is the template section for a component and below is the script.
<script>
import Chart from 'vue-bulma-chartjs'
import { Tabs, TabPane } from 'vue-bulma-tabs'
export default {
components: {
Chart
},
data () {
return {
persondata: {},
}
},
mounted () {
let parameter = this.$route.params.id
axios.get('/api' + parameter.toString()).then(response => {
this.persondata = response.data
})
},
computed: {
lineData () {
var sth = this.person.dates['date-values']
return {
labels: [9, 10, 11, 12, 13, 14, 15],
datasets: [{
data: sth,
label: 'Speed',
}]
}
}
}
</script>
So as you see this Vue component renders a chart from Chart.js on the page. The problem is when I get the response from the api and save it to the this.persondata variable, when the component is mounted I get "TypeError: this.persondata.dates is undefined". If I do this though:
data () {
return {
persondata: {
dates: {
'date-values': []
}
}
}
}
and I try to save the response.data.dates['date-values'] to this.persondata.dates['date-values'] and insert it into the dataset.data in computed() I don't get any chart.
What is the problem?
computed properties are calculated right away: they are calculated before your Ajax returns.
Since you only populate persondata when the Ajax returns, the first time the computed is calculated, persondata is {} (the value of initialization in data).
Quick fix: initialize with an empty dates object, so the computation doesn't throw that error (giving time for the Ajax to return, which will update the computed property automatically):
data () {
return {
persondata: {dates: {}}, // added empty dates property
}
},

How to access vuejs method from inside an object ? (Vuejs 2 )

I want to access getValue method from Chart object, but I get function undefined.
<template>
<div>
<canvas width="600" height="400" ref="canvas"></canvas>
</div>
</template>
<script>
import Vue from 'vue';
import Chart from 'chart.js';
import Axios from 'axios';
export default {
mixins: [DateRangeMixin],
props: {
// other props...
callback: false,
},
data() {
return {
chart: '',
};
},
mounted() {
// ...
},
methods: {
//other methods...,
getValue(data) {
if (data === 1) {
return 'Up'
} else if(data === 0) {
return 'Down';
}
},
render(data) {
this.chart = new Chart(this.$refs.canvas, {
type: 'line',
data: {
labels: Object.keys(data),
datasets: [{
// a lot of data ....
data: Object.values(data),
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
callback(label, index, labels) {
return this.getValue(label); // <-- Tried this and got: 'this.getValue is not a function'. I understand it bounces to new Chart object, but how to resolve this?
}
}
}]
}
}
});
},
},
};
</script>
I understand that it's because Chart is an object and this is pointing to it, but how do I resolve this and access my method from the callback ?
I imagine if that export default... would be set to a variable, then I could access my method via variable.methods.getValue , but in this scenario How can I achieve my goal ?
Right before you create the new Chart() assign this to a variable self: var self = this;.
You can then access your component properties throughself.

Categories