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>
Related
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,
},
})
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>
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();
I created a chart using vue-chartjs which renders some data from a JSON endpoint.
The problem is that the chart is not rendered after the page reloads.
This is the HTML that calls the chart component.
<absentee-chart v-if="isLoaded==true" :chart-data="this.chartData"></absentee-chart>
This is the absenteeChart.js file that renders the chart data.
import { Bar, mixins } from 'vue-chartjs'
export default {
extends: Bar,
mixins: [mixins.reactiveProp],
data: () => ({
options: {
responsive: true,
maintainAspectRatio: false
}
}),
mounted () {
this.renderChart(this.chartData, this.options)
}
}
And finally my .vue file.
created () {
axios
.get(constants.baseURL + "absentee-reports/graph", auth.getAuthHeader())
.then(response => {
this.graph = response.data;
var males = {
data: []
};
var females = {
data: []
};
var len = this.graph.length;
for (var i = 0; i < len; i++) {
if (this.graph[i].year == "2009") {
this.labels.push(this.graph[i].month);
//push to males
this.datasets[0].data.push(this.graph[i].males);
//push to females
this.datasets[1].data.push(this.graph[i].females);
}
}
this.isLoaded = true;
this.chartData.labels = this.labels;
this.chartData.datasets = this.datasets;
});
}
UPDATE: The chart appears after I resize my browser page.
The solution to this was separating all my chart logic from my .vue file and omitting the use of mixins and reactiveProp. From what I could tell, this issue lies deep within the core of chart.js
Hope this helps somebody.
Cheers :)
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.