How to update a chart using VueJS and ChartJS - javascript

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();

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,
},
})

Updating Chartjs Data with Response from POST Call?

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}
/>

How to update chart properties using the chart instance in vuejs + chartjs?

I am following this tutorial, by Alligator, which explains how to create a basic line graph with vue js and chart js. The code works by defining a createChart vue method, importing chart configurations,planetChartData, and then calling the method once the vue instance is mounted to create a graph instance.
I, however, am interested in updating the line chart with new data points once an initial chart has been rendered to the html canvas element.
According to the chart js docs, a chart can be updated with the following function.
function addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach((dataset) => {
dataset.data.push(data);
});
chart.update();
}
I took the function and decided to turn it into a vue method to update charts. Since coordinate data is stored in the data object I can directly modify the planetChartData like so, but my issue is that I'm unsure of what to pass as the chart parameter to rerender the chart once the arrays are updated, since the myChart instance is out of scope. I tried initializing myChart in other places but that always gave tons of errors.
addAtempt(chart, label, data){
this.lineChartData.data.labels.push('label')
this.lineChartData.data.datasets.forEach(dataset => {
dataset.data.push(data);
});
//issue is here
chart.update()
}
Below is my full vue component
<template>
<div>
<h2>Fun Graph</h2>
<canvas id="planet-chart"></canvas>
</div>
</template>
<script>
import Chart from "chart.js";
import planetChartData from "./chart-data.js";
export default {
name: "test",
data() {
return {
text: "",
planetChartData: planetChartData
};
},
mounted() {
this.createChart("planet-chart", this.planetChartData);
},
methods: {
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
const myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options
});
},
addData(chart, label, data) {
chart.data.labels.push(label);
chart.data.datasets.forEach(dataset => {
dataset.data.push(data);
});
}
}
};
</script>
You have to save a reference to the instance of your chart, namely in your createChart method.
Full working example on CodeSandbox.
<template>
<div>
<h2>Fun Graph</h2>
<canvas id="planet-chart"></canvas>
</div>
</template>
<script>
import Chart from "chart.js";
import planetChartData from "./chart-data.js";
export default {
name: "test",
data() {
return {
text: "",
planetChartData: planetChartData,
myChart: null,
};
},
mounted() {
this.createChart("planet-chart", this.planetChartData);
},
methods: {
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
// Save reference
this.myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options
});
},
addData(label, data) {
// Use reference
this.myChart.data.labels.push(label);
this.myChart.data.datasets.forEach(dataset => {
dataset.data.push(data);
});
}
}
};
</script>

Create dynamically updating graph with Angular 6 on API calls

I am trying to implement angular chart.js graph dynamically update based on api calls
Currently i have implemented it to be a static chart, as follows.
service
import { Injectable } from '#angular/core';
import {HttpClient,HttpHeaders} from '#angular/common/http';
import 'rxjs/add/operator/map';
#Injectable({
providedIn: 'root'
})
export class WeatherService {
constructor(private _http:HttpClient) {
}
dailyForecast(){
return this._http.get("http://samples.openweathermap.org/data/2.5/history/city?q=Warren,OH&appid=b6907d289e10d714a6e88b30761fae22")
.map(result => result);
}
}
component
ngOnInit(){
this._weather.dailyForecast()
.subscribe(res=>{
let temp_max = res['list'].map(res=> res.main.temp_max)
let temp_min = res['list'].map(res=> res.main.temp_min)
let alldates = res['list'].map(res=> res.dt)
let weatherDates = []
alldates.forEach((res) => {
let jsdate = new Date(res*1000)
weatherDates.push(jsdate.toLocaleTimeString('en',{year:'numeric',month:'short',day:'numeric'}))
});
this.chart = new Chart('canvas',{
type: 'line',
data : {
labels:weatherDates,
datasets: [
{
data:temp_max,
borderColor: '#3cba9f',
fill:false
},
{
data:temp_min,
borderColor: '#ffcc00',
fill:false
}
]
},
options:{
legend:{
display:false
},
scales: {
xAxes: [{
display:true
}],
yAxes: [{
display:true
}]
}
}
});
})
}
But i wanted it to make , look like IQ Option like graph(To update that graph component only) like this,
dynamically updating graph
what are the options that i can take? and how can i implement those?
You can do it through polling service, where you make each request after a specific interval,
You can make a request based on the interval using $interval,
Observable.interval(3000)
or
let intervalId = setInterval(() => {
this.updateData();
}, 3000);

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