I always get this error when I update the line chart data. I know that setState is wrong, but the specific modification method cannot be found, and ask for help. Thank you.
I think it's the problem, but there's no solution.
this.setState({
data: {
...this.state.data.$set,
dataSets:[{
...this.state.data.$set.dataSets[0],
values
}]
},
xAxis: {
...this.state.xAxis.$set.valueFormatter,
valueFormatter,
axisMaximum,
axisMaximum
},
})
Chart github address:
https://github.com/wuxudong/react-native-charts-wrapper
This is the source code, just uploaded the picture, the feeling is not suitable for reading.
componentDidMount() {
this.setState(
update(this.state, {
data: {
$set: {
dataSets: [{
values: [{y: 10}, {y: 20}, {y: 30.11}, {y: 25.77}],
label: 'LineChart',
config: {
lineWidth: 0.5,
drawCircles: false,
highlightColor: processColor('red'),
color: processColor('red'),
drawFilled: true,
fillColor: processColor('red'),
fillAlpha: 60,
valueTextSize: 15,
valueFormatter: "##.000",
}
}],
}
},
xAxis: {
$set: {
fontFamily:"HelveticaNeue-Medium",
fontWeight:"bold",
fontStyle:"italic",
valueFormatter: ['2017-01-01 10:00:000','2017-02-01 10:00:000','2017-03-01 10:00:000'],
position: 'BOTTOM' ,
drawGridLines: false
}
},
yAxis: {
$set: {
left: {
drawLabels: true,
drawAxisLine: true,
drawGridLines: false,
drawValueAboveBar:false,
zeroLine: {
enabled: true,
lineWidth: 1
}
},
right: {
enabled: false
}
}
},
})
);
}
componentWillReceiveProps(nextProps) {
debugger;
if (nextProps.zxData !== this.props.zxData) {
let values=[];
let valueFormatter=[];
let axisMaximum=nextProps.zxData.length;
nextProps.zxData.map((item,key)=>{
values.push({y:item.YValue});
valueFormatter.push(item.XValue);
})
this.setState({
data: {
...this.state.data.$set,
dataSets:[{
...this.state.data.$set.dataSets[0],
values
}]
},
xAxis: {
...this.state.xAxis.$set.valueFormatter,
valueFormatter,
axisMaximum,
axisMaximum
},
})
}
}
Related
I'm trying to make an area chart using apexcharts with nuxt (vue2), but the area is not getting filled, and the options i set in chartOptions for fill are getting used by the line itself instead of the area, as below:
this is my apexchart component:
<client-only>
<apexcharts
type="area"
width="70%"
ref="realtimeChart"
:options="chartOptions"
:series="series">
</apexcharts>
</client-only>
this is my chartOptions:
chartOptions: {
chart: {
toolbar: {
show: true,
tools: {
download: true,
selection: false,
zoom: true,
zoomin: false,
zoomout: false,
pan: false,
reset: false,
},
},
id: "basic-bar",
colors: ["#FFA07A"],
fill: {
type: "gradient",
gradient: {
shadeIntensity: 1,
opacityFrom: 0.7,
opacityTo: 0.9,
stops: [0, 100],
},
},
forecastDataPoints: {
count: 28,
strokeWidth: 4,
dashArray: 5,
},
title: {
text: "doesnt matter",
align: "center",
},
grid: {
borderColor: "#e7e7e7",
row: {
colors: ["#f3f3f3", "transparent"],
opacity: 0.5,
},
},
xaxis: {
type: "date",
tickAmount: 40,
},
yaxis: [
{
title: {
text: "Mt CO2eq/we",
},
forceNiceScale: true,
},
]
},
Can anyone help ?
I was declaring type: "line" in my series. thats all.
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'm new with vue and apex charts, basically what I need is to call a method from the apex chart options, I created a file showing the problem I'm having:
https://jsfiddle.net/wr3uo5va/
I need to call the method currencyValue from chartOptions.dataLabels
dataLabels: {
enabled: true,
offsetX: -25,
formatter: function(val) {
return val + " Reais"; <--- This works
// return this.currencyValue(val) <--- This does not work
},
},
Any suggestion ?
The problem is this inside the formatter callback is the chart instance (not the component instance) because it's declared as a regular function.
The solution is to use an arrow function to bind the component instance as the context:
export default {
methods: {
currencyValue(value) {⋯},
loadChartData() {
⋮
this.chartOptions = {
⋮
dataLabels: {
⋮
// ❌ don't use regular function here
//formatter: function(val) {
// return this.currencyValue(val)
//},
// ✅
formatter: (val) => {
return this.currencyValue(val)
},
},
}
}
}
}
updated fiddle
You can put chartOptions in methods instead of in data.
Below is working code
const currencyValue = (val) => {
return "R$" + val;
}
new Vue({
el: "#app",
data() {
return {
series: [450, 300, 500]
}
},
methods: {
chartOptions() {
return {
labels: ['Paid', 'Pending', 'Rejected'],
plotOptions: {
radialBar: {
size: 165,
offsetY: 30,
hollow: {
size: '20%'
},
track: {
background: "#ebebeb",
strokeWidth: '100%',
margin: 15,
},
dataLabels: {
show: true,
name: {
fontSize: '18px',
},
value: {
fontSize: '16px',
color: "#636a71",
offsetY: 11
},
total: {
show: true,
label: 'Total',
formatter: function() {
return 42459
}
}
}
},
},
responsive: [{
breakpoint: 576,
options: {
plotOptions: {
radialBar: {
size: 150,
hollow: {
size: '20%'
},
track: {
background: "#ebebeb",
strokeWidth: '100%',
margin: 15,
},
}
}
}
}],
colors: ['#7961F9', '#FF9F43', '#EA5455'],
fill: {
type: 'gradient',
gradient: {
// enabled: true,
shade: 'dark',
type: 'vertical',
shadeIntensity: 0.5,
gradientToColors: ['#9c8cfc', '#FFC085', '#f29292'],
inverseColors: false,
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100]
},
},
stroke: {
lineCap: 'round'
},
chart: {
dropShadow: {
enabled: true,
blur: 3,
left: 1,
top: 1,
opacity: 0.1
},
},
tooltip: {
x: {
formatter: function (val) {
return val;
},
},
y: {
formatter: function (val) {
return currencyValue(val);
},
},
},
}
}
},
components: {
VueApexCharts
}
})
Methods can't be called in data or computed, they can be called in methods
One thing to be modified in html is below
<vue-apex-charts
type="donut"
:options="chartOptions()"
:series="series">
</vue-apex-charts>
I have a bar chart and I would like to change the font color, border width and some other tings, but it doesn't work. It is in my computed property. In the chartOptions I want to change the y axis min and max value but I don't know if it is correct. Can anyone help me?
Also I want to make a horizontal line in this bar chart. It is the "Enemy's Avarage" and now it is a constant. I set the type to line and now I have a dot in my chart.
This is my chart component:
<script>
import { defineComponent } from 'vue'
import { Bar } from 'vue3-chart-v2'
export default defineComponent({
name: 'ChanceChart',
extends: Bar,
props: {
chartData: {
type: Object,
required: true
},
chartOptions: {
type: Object,
required: false,
},
},
mounted () {
this.renderChart(this.chartData, this.chartOptions)
}
})
</script>
And this is my app:
<template>
<div id="chart">
<ChanceChart :chartData="chartData" :chartOptions="chartOptions" />
</div>
</template>
<script>
import Navigation from "../components/Navigation.vue";
import ChanceChart from "../components/ChanceChart.vue";
import PageLoader from "../components/PageLoader.vue";
export default {
components: {
ChanceChart,
},
computed: {
chartOptions() {
return {
options: {
scales: {
y: {
title: {
display: true,
text: 'Value'
},
min: 0,
max: 100,
ticks: {
stepSize: 10,
}
},
},
elements: {
point: {
radius: 0
},
line: {
borderWidth: 2
}
},
plugins: {
legend: {
labels: {
boxWidth: 0,
font: {
fontColor: "#fff",
color: "#fff"
},
},
},
},
}
}
},
chartData() {
return {
datasets: [
{
type: 'bar',
label: "Enemy's Chance",
borderColor: "#1161ed",
borderWidth: 2,
data: this.emnemyCardsFilled,
},
{
type: 'bar',
label: "My Chance",
borderColor: "#f87979",
borderWidth: 2,
data: this.myCardsFilled,
},
{
type: 'line',
label: "Enemy's Avarage",
borderColor: "rgb(238, 255, 0)",
borderWidth: 2,
data: [50],
},
],
}
},
},
You need to remove the options part in the computed chartOptions prop:
chartOptions() {
return {
scales: {
y: {
title: {
display: true,
text: 'Value'
},
min: 0,
max: 100,
ticks: {
stepSize: 10,
}
},
},
elements: {
point: {
radius: 0
},
line: {
borderWidth: 2
}
},
plugins: {
legend: {
labels: {
boxWidth: 0,
font: {
fontColor: "#fff",
color: "#fff"
},
},
},
},
}
},
the issue im facing is when i end 2nd chart like 'chart.render()' , 2nd chart gets value that i selected in first chart but when i click on another value in first chart , instead of update 2nd chart div , it creats another div vertically.
here is my chart.
i'm getting value from controlle in chart.
var app = #json($newval);
var options = {
series: app,
chart: {
events: {dataPointSelection:function (event, chartContext, config) {
var value= config.w.config.series[config.dataPointIndex]
$( "#chart2" ).load(window.location.href + " #chart2" );
secondchart(value);
}
},
width: 380,
type: 'donut',
},
dataLabels: {
enabled: false
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
},
legend: {
show: false
}
}
}],
legend: {
position: 'right',
offsetY: 0,
height: 230,
}
};
var chart = new ApexCharts(document.querySelector("#chart1"), options);
chart.render();
//second chart starts...
function secondchart(value) {
//in this console.log im getting correct value
console.log(value)
var options = {
series: [{
data: [value]
}],
chart: {
type: 'bar',
height: 350
},
plotOptions: {
bar: {
borderRadius: 4,
horizontal: true,
}
},
dataLabels: {
enabled: false
},
xaxis: {
categories: ['South Korea', 'Canada', 'United Kingdom', 'Netherlands', 'Italy', 'France', 'Japan',
'United States', 'China', 'Germany'
],
}
};
chart.render()
}
</script>```
so here is code what i want do do exactly,
<script>
var app = #json($newval);
var options = {
series: app,
chart: {
events: {
dataPointSelection: function (event, chartContext, config) {
var value = config.w.config.series[config.dataPointIndex]
secondchart(value);
}
},
width: 380,
type: 'donut',
},
dataLabels: {
enabled: false
},
responsive: [{
breakpoint: 480,
options: {
chart: {
width: 200
},
legend: {
show: false
}
}
}],
legend: {
position: 'right',
offsetY: 0,
height: 230,
}
};
var chart = new ApexCharts(document.querySelector("#chart1"), options);
chart.render();
function secondchart(value) {
var cat = #json($cat);
var options = {
series: [{
data: [value,value]
}],
chart: {
redrawOnParentResize: true,
type: 'bar',
height: 350
},
plotOptions: {
bar: {
borderRadius: 4,
horizontal: true,
}
},
dataLabels: {
enabled: false
},
xaxis: {
categories: [cat
],
}
};
// $( "#chart2" ).load(window.location.href + " #chart2" );
var chart = new ApexCharts(document.querySelector("#chart2"), options);
debugger
if ($("#chart2") == null) {
console.log( $("#chart2"));
chart.render()
} else {
$("#chart2").empty();
console.log( $("#chart2"));
chart.render()
}
}
</script>