I have difficult situation with updates dynamically data on charts. I created vue component that must render analytics from axios. There are several functions (methods) which parse arrived jsons from several API.
I created draw() - method for render every charts.
draw() {
if (this.mychart) {
this.mychart.destroy();
}
const ctx = document.getElementById('main-chart');
this.mychart = new Chart(ctx,
{
type: 'line',
data: {
labels: this.labels,
datasets: this.datacollection
},
options: {
legend: {
display: true,
position: 'bottom',
},
responsive: true,
scales: {
xAxes: [{
type: "time",
display: true,
scaleLabel: {
display: false,
},
ticks: {
major: {
fontStyle: "bold",
fontColor: "#FF0000"
}
}
}],
yAxes: [
{
id: 'y1',
type: 'linear',
position: 'left',
display: false
},
{
id: 'y2',
type: 'linear',
position: 'right',
display: false
},
{
id: 'y3',
type: 'linear',
position: 'left',
display: false
},
{
id: 'y4',
type: 'linear',
position: 'right',
display: false
},
{
id: 'y5',
type: 'linear',
position: 'left',
display: false
},
{
display: false,
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: this.labelY
},
ticks: {
min: 0, // it is for ignoring negative step.
beginAtZero: true,
stepSize: 100
}
}]
}
}
});
},
All operations for calculating (parsing) data I do in mounted hook.
Also added in mounted hook nextTick() for delay render charts before data finish parsing.
async mounted() {
await this.loadIncomings(this.clubsId),
await this.loadAvgIncomings(this.clubsId),
await this.loadAvgPayments(this.clubsId),
await this.loadAvgSchedule(this.clubsId),
await this.loadTrainings(this.clubsId)
this.$nextTick(() => {
this.draw()
})
},
Now if internet connection is fast - charts render at one time. If you refresh page - you must waiting approximately 5-20 seconds for parsing data, and after this - appear graphs.
But, if I have bad connection, some axios requests finish with errors, and appear not all charts. And also I must waiting longer for parsing.
Finally, I have situation, that when I refresh page - several seconds page is empty. If connection is bad- not all charts render.
After this I have several questions:
1)How I could start render some finished data by chartjs in first seconds?
I mean not waiting when all data will come and calculated. I would like , that my charts render step by step. I want see y-axis and x-axis after I click refresh window.
Now I am using nextTick() but it is like 2nd step, where 1st step - is parsing data (may be I don't correctly understand)
I found some answers on similar question with render dynamic data, and people offered use chart.update(). But I can't get it. Where I must input this? If you look at my component, I have special method - draw(). If input in final string in my method with parsing data like : this.draw.mychart.update() or this.mychart.update() - I receive error in browser.
For example this function:
async loadIncomings(clubsId) {
try {
for (let clubId in clubsId) {
clubId = clubsId[clubId]
let dateFrom = this.dateFrom
let dateTo = this.dateTo
let groupBy = 'month'
let potential = true
let definitely = true
await this.$store.dispatch('loadIncomings', { clubId, dateFrom, dateTo, groupBy, potential, definitely }) // here I am waiting data from store (in store I use axios)
this.draftData = this.$store.state.incomings
if (this.labels.length === 0) {
this.getDates()
}
this.flagStartDate = true
await this.getIncomings(clubId)
this.flagStartDate = false
}
this.getIncomingsTotal()
this.draw.mychart.update() // here I am trying to refresh charts like advice from forums
} catch (e) {
console.error(e)
}
},
As you can see, this.getIncomingsTotal() - is last method for parsing data. After him, I am trying to update chart. But it's doesn't work.
Analytics-test.vue?b2a7:177 TypeError: Cannot read properties of undefined (reading 'update')
at VueComponent.loadIncomings (Analytics-test.vue?b2a7:175:1)
at async VueComponent.mounted (Analytics-test.vue?b2a7:498:1)
2)Also I use vue2-datepicker. I want set range dateFrom/dateTo. But when I choose date - charts doesn't change.
I have watch() , where I can monitoring dates dateFrom() and dateTo(). And also I am trying to rechart graphs with new dates - but nothing changes.
watch: {
dateFrom() {
console.log('dateFrom changed to', this.dateFrom)
this.draw()
},
dateTo() {
console.log('dateTo changed to', this.dateTo)
this.draw()
}
},
Under I show you my component:
<template>
<div class="container">
<h3>Прибыль/посещаемость</h3>
<date-picker v-model="dateFrom" valueType="date"></date-picker>
<date-picker v-model="dateTo" valueType="date"></date-picker>
<canvas id="main-chart"></canvas>
</div>
</template>
<script>
import Chart from 'chart.js';
import DatePicker from 'vue2-datepicker';
import 'vue2-datepicker/index.css';
export default {
name: 'Analytics-test',
components: {
DatePicker
},
data: () => ({
dateFrom: new Date('2021-12-01'),
dateTo: new Date(),
flagStartDate: false,
chartData: null,
labels: [],
dataset: {},
draftData: null,
data: [],
datacollection: [],
clubsId: ['5c3c5e12ba86198828baa4a7', '5c3c5e20ba86198828baa4c5', '60353d6edbb58a135bf41856', '61e9995d4ec0f29dc8447f81', '61e999fc4ec0f29dc844835e'],
}),
methods: {
draw() {
if (this.mychart) {
this.mychart.destroy();
}
const ctx = document.getElementById('main-chart');
this.mychart = new Chart(ctx,
{
type: 'line',
data: {
labels: this.labels,
datasets: this.datacollection
},
options: {
legend: {
display: true,
position: 'bottom',
},
responsive: true,
elements: {
line: {
// tension: 0, // disables bezier curves
// bezierCurve: false
}
},
scales: {
xAxes: [{
type: "time",
display: true,
// gridLines: {
// display: false
// },
scaleLabel: {
display: false,
// labelString: 'Time'
},
ticks: {
major: {
fontStyle: "bold",
fontColor: "#FF0000"
}
}
}],
yAxes: [
{
id: 'y1',
type: 'linear',
position: 'left',
display: false
},
{
id: 'y2',
type: 'linear',
position: 'right',
display: false
},
{
id: 'y3',
type: 'linear',
position: 'left',
display: false
},
{
id: 'y4',
type: 'linear',
position: 'right',
display: false
},
{
id: 'y5',
type: 'linear',
position: 'left',
display: false
},
{
display: false,
gridLines: {
display: false
},
scaleLabel: {
display: true,
labelString: this.labelY
},
ticks: {
min: 0, // it is for ignoring negative step.
beginAtZero: true,
stepSize: 100 // if i use this it always set it '1', which look very awkward if it have high value e.g. '100'.
}
}]
}
}
});
},
// Выручка
incomingsClub(clubId) {
switch (clubId) {
case '5c3c5e12ba86198828baa4a7':
return { label: "Выручка Фрунзенской", borderColor: "#3e95cd", fill: false }
case '5c3c5e20ba86198828baa4c5':
return { label: "Выручка Чернышевской", borderColor: "#8e5ea2", fill: false };
case '60353d6edbb58a135bf41856':
return { label: "Выручка Василеостровской", borderColor: "#e8c3b9", fill: false };
case '61e9995d4ec0f29dc8447f81':
return { label: "Выручка Московской", borderColor: "#3cba9f", fill: false };
case '61e999fc4ec0f29dc844835e':
return { label: "Выручка Лесной", borderColor: "#c45850", fill: false };
case 'all':
return { label: "Выручка сети", borderColor: "#8e8786", fill: false };
default:
return 'Неизвестный клуб';
}
},
async loadIncomings(clubsId) {
try {
for (let clubId in clubsId) {
clubId = clubsId[clubId]
let dateFrom = this.dateFrom
let dateTo = this.dateTo
let groupBy = 'month'
let potential = true
let definitely = true
await this.$store.dispatch('loadIncomings', { clubId, dateFrom, dateTo, groupBy, potential, definitely })
this.draftData = this.$store.state.incomings
if (this.labels.length === 0) {
this.getDates()
}
this.flagStartDate = true
await this.getIncomings(clubId)
this.flagStartDate = false
}
this.getIncomingsTotal()
// this.draw.mychart.update()
} catch (e) {
console.error(e)
}
},
getDates() {
for (let item in this.draftData) {
if (item === 'items') {
for (let elem in this.draftData[item]) {
this.labels.push(this.draftData[item][elem].date.slice(0, 7))
}
}
}
},
bindDataDates(indexDate) {
return Array(indexDate).fill(null);
},
getIncomings(clubId) {
for (let item in this.draftData) {
if (item === 'items') {
for (let elem in this.draftData[item]) {
let positionDate = this.labels.indexOf(this.draftData[item][elem].date.slice(0, 7))
if (this.flagStartDate && positionDate > 0) {
let zerroArray = this.bindDataDates(positionDate)
this.data = this.data.concat(zerroArray)
}
this.data.push(this.draftData[item][elem].amount)
this.flagStartDate = false
}
this.dataset.data = this.data
Object.assign(this.dataset, this.incomingsClub(clubId))
Object.assign(this.dataset, { yAxisID: 'y1' })
this.datacollection.push(this.dataset)
this.data = []
this.dataset = {}
}
}
},
getIncomingsTotal() {
for (let item in this.datacollection) {
if (!this.data.length) {
this.data = this.datacollection[item].data
continue
}
const firstArr = this.data
const secondArr = this.datacollection[item].data;
this.data = []
let length;
if (firstArr.length >= secondArr.length) {
length = firstArr.length;
} else {
length = secondArr.length;
}
for (let i = 0; i < length; i++) {
const a = firstArr[i] === undefined ? 0 : firstArr[i];
const b = secondArr[i] === undefined ? 0 : secondArr[i];
this.data.push(a + b);
}
}
this.dataset.data = this.data
Object.assign(this.dataset, this.incomingsClub('all'))
Object.assign(this.dataset, { yAxisID: 'y1' })
this.datacollection.push(this.dataset)
this.data = []
this.dataset = {}
},
// Средняя сумма за жизнь
avgIncomingsClub(clubId) {
omitted..
},
getDatesAvgIncome() {
omitted..
},
async loadAvgIncomings(clubsId) {
omitted..
},
getAvgIncomings(clubId) {
omitted..
},
// Среднее кол-во оплат
avgPaymentsClub(clubId) {
omitted..
},
async loadAvgPayments(clubsId) {
omitted..
},
getAvgPayments(clubId) {
omitted..
},
// Посещаемость
avgAttendanceClub(clubId) {
omitted..
},
async loadAvgSchedule(clubsId) {
omitted..
},
getAvgAttendance(clubId) {
omitted..
},
// Тренировок
participantsCountClub(clubId) {
omitted..
},
async loadTrainings(clubsId) {
omitted..
},
async getParticipantsCount(clubId) {
omitted..
},
watch: {
dateFrom() {
console.log('dateFrom changed to', this.dateFrom)
this.draw()
},
dateTo() {
console.log('dateTo changed to', this.dateTo)
this.draw()
}
},
async mounted() {
await this.loadIncomings(this.clubsId),
await this.loadAvgIncomings(this.clubsId),
await this.loadAvgPayments(this.clubsId),
await this.loadAvgSchedule(this.clubsId),
await this.loadTrainings(this.clubsId)
this.$nextTick(() => {
this.draw()
})
},
</script>
<style>
.container form {
display: flex;
}
</style>
I skipped code in other functions because it's doesn't matter. Situation with other the same.
I have created a project with Vue. I'm trying to show some data through a chart using the ChartJs library, to capture the information I use axios. I've been getting this error since the beginning but I don't know what I'm missing, any idea?
async created() {
const { data } = await axios.get("https://covid19.mathdro.id/api/confirmed");
var c=0
for(var i=0;i<1000;i++){
if(data[i].countryRegion=="India"){
if (data[i].provinceState in this.labels){
continue
}
else{
this.labels.push(data[i].provinceState)
this.confirmed.push(data[i].confirmed)
c=c+1
if(c==28){
break
}
}
}
}
console.log(this.labels)
}
In the error message it also mentions this function which is in the LineChart.vue file
<template>
<canvas ref="chartLine" width="900px" height="250px"></canvas>
</template>
<script>
import Chart from 'chart.js';
export default {
props: {
label: {
type: Array
},
chartData: {
type: Array
},
},
async mounted() {
await new Chart(this.$refs.myChart, {
type: 'line',
data: {
labels: this.label,
datasets: [
{
label: 'CASES',
borderColor: 'rgba(245, 229, 27, 1)',
backgroundColor: 'rgba(255, 236, 139,0.2)',
data: this.chartData,
}
]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: true
}
}]
}
}
});
}
}
</script>
I want to create pie-chart with vue-apexcharts. I had data from API but I don't know how to update the chart's data.
mounted() {
axios
.get("/data/episodes.json")
.then(response => {
console.log(response);
});
}
The chart I want to create is like this
Here is my understanding on doing, ...
first you have to define the data and option values of chart.
components: {
apexchart: VueApexCharts,
},
data: {
series: [],
chartOptions: {
chart: {
width: 380,
type: "pie",
},
labels: [],
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
legend: {
position: "bottom",
},
},
},
],
},
},
Now on mount you have call your api to get the data and update the chart information. like this ...
I'm thinking of you server would return an array of objects having value and key.
mounted() {
axios.get("/data/episodes.json").then((response) => {
for (let i = 0; i < response.data.length; i++) {
this.data.series.push(response.data[i].value);
this.data.chartOptions.labels.push(response.data[i].key);
}
});
},
Finally, you have to add the chart component to the vue template. like this.
<div id="chart">
<apexchart type="pie" width="380" :options="chartOptions" :series="series"></apexchart>
</div>
Done,
Ask me if it is not clear for you.
Im trying to display chart using chartjs by calling API, but unable to do so.
Here s my LineChart.vue:
<script>
import {Line, mixins} from 'vue-chartjs' // We specify what type of chart we want from vue-chartjs and the mixins module
const { reactiveProp } = mixins
export default { //We are extending the base chart class as mentioned above
extends: Line,
mixins: [reactiveProp],
data () {
return {
options: { //chart options
lineTension: 0,
maintainAspectRatio: false,
legend: {
display: false
},
scales: {
yAxes: [
{
scaleLabel: {
display: false
},
ticks: {
beginAtZero: true,
// eslint-disable-next-line no-unused-vars
callback (value, index, values) {
return `${value }%`
}
}
}
],
xAxes: [
{
type: 'time',
time: {
unit: 'month'
},
scaleLabel: {
display: true,
labelString: ''
}
}
]
}
}
}
},
mounted () {
// this.chartData is created in the mixin
this.renderChart(this.chartData, this.options)
}
}
</script>
And here is my Home.vue where i have imported the LineChart:
<template>
<div class="chart">
<line-chart :chart-data="datacollection"></line-chart>
</div>
</template>
<script>
import LineChart from './LineChart'
import axios from 'axios'
import DateTime from 'luxon'
export default {
data () {
return {
date: {},
challenge: {},
datacollection: {}
}
},
component: {LineChart},
created() {
this.fillData()
},
mounted () {
this.fillData()
},
methods: {
fillData () {
axios.get('https://my_api_goes_here')
.then(response => {
const results = response.data
const dateresult = results.map(a => a.date)
const challengeresult = results.map(a => a.challenge)
this.date = dateresult
this.challenge = challengeresult
this.datacollection = {
labels: [this.date].map(labels => DateTime.fromMillis(labels * 1000).toFormat('MMM yyyy')),
datasets: [
{
data: [this.challenge],
label: 'Africa',
borderColor: '#7367F0'
}
]
}
})
.catch(error => {
console.log(error)
})
}
}
}
</script>
Dont know why the chart did not appear even though my other resources have been loaded from the API call, when i checked out my console, this is what error im getting:
TypeError: results.map is not a function
Please check out my logic and let me where the error is.
I am writing a small covid project and trying to plot confirmed infection data using ApexCharts, but the graph is not showing. I enter the data from vuex in two tables. The data is valid however it comes from api and sa in the proxy object. What am I doing wrong? (I am using ApexCharts because vue Chartjs is not compatible with vue 3).
<template>
<apexchart width="500" type="bar" :options="options" :series="series"></apexchart>
</template>
<script>
import VueApexCharts from "vue3-apexcharts";
export default {
components: {
apexchart: VueApexCharts,
},
data(){
return {
series: [],
options: {
chart: {
type: "bar",
height: 400,
stacked: true
},
plotOptions: {
bar: {
horizontal: false
}
},
dataLabels: {
enabled: false
},
tooltip: {
shared: true,
followCursor: true
},
stroke: {
width: 1,
colors: ["#fff"]
},
fill: {
opacity: 1
},
legend: {
position: "top",
horizontalAlign: "center",
offsetX: 40
},
colors: ["rgb(95, 193, 215)", "rgb(226, 37, 43)", "rgb(94, 181, 89)"]
}
};
},
computed: {
getDate(){
return this.$store.getters['chart/getDate'];
},
getConfirmed(){
return this.$store.getters['chart/getConfirmed'];
}
},
methods: {
fillDate(){
this.options = {
xaxis: {
categories: this.getDate
}
}
this.series = [
{
name: 'Confirmed',
data: this.getConfirmed
}
];
}
},
async mounted() {
await this.fillDate();
}
}
The data from the vuex store are two arrays.
Proxy
[[Handler]]: Object
[[Target]]: Array(45)
[[IsRevoked]]: false
Instead of using mounted hook and method try to watch the computed properties then update the data based on that ones:
computed: {
getDate(){
return this.$store.getters['chart/getDate'];
},
getConfirmed(){
return this.$store.getters['chart/getConfirmed'];
}
},
watch:{
getDate:{
handler(newVal){
this.options = {
xaxis: {
categories: this.getDate
}
},
deep:true
},
getConfirmed:{
handler(newVal){
this.series = [
{
name: 'Confirmed',
data: this.getConfirmed
}
];
},
deep:true
}
}