I have this Chart component. useEffect works as componentDidMount() so it should first render the DOM and then should call the fetchChartData. But the issue I am facing here is I get null in console.log("111111111", document.getElementById("data-consumed")) at first and after that I get the DOM elements and resulting I get error from the Highcharts
Uncaught (in promise) Error: Highcharts error #13
So I need to know why this is happening as useEffect works as componentDidMount and should render the DOM first.
const Chart2 = (props) => {
const fetchChartData = async () => {
const { fetchChartData } = props;
const startDate = moment().startOf("year").toISOString();
const endDate = moment().endOf("year").toISOString();
const data = await fetchChartData({ startDate, endDate, userId: "XXXXXXX" });
console.log("111111111", document.getElementById("data-consumed"))
Highcharts.chart("data-consumed", {
chart: {
type: "column",
},
title: {
text: null,
},
subtitle: {
text: "Source: WorldClimate.com",
},
xAxis: {
categories: data.map(({ _id }) => _id),
crosshair: true,
},
yAxis: {
min: 0,
title: {
text: "Rainfall (mm)",
},
},
plotOptions: {
column: {
pointPadding: -0.1,
borderWidth: 0,
color: "#5036D6",
},
},
series: [
{
name: null,
data: data.map(({ processingStorage }) => processingStorage),
},
],
});
};
useEffect(() => {
fetchChartData();
}, []);
return (
<div className="chart-container cursor-pointer">
<div className="chart-block">
<h4>Data Consumed</h4>
<div id="data-consumed" className="graph-block" />
</div>
</div>
);
};
export default Chart2;
Try using a setTimeout function. Call the fetchChartData function inside setTimeout
useEffect(() => {
setTimeout(()=> {
fetchChartData();
}, 2000)
}, []);
Related
I working on a page where I let users select some criteria, which I use later to extract data from the DB and display them as a chart (apexcharts)
Everything is working fine, except options.xaxis.categories which are changing (number and names) based on the selection ...
what I am doing is, that I have this method for data gathering
import VueApexCharts from 'vue-apexcharts';
...
components: { apexchart: VueApexCharts },
...
DataService.getData(this.params).then(
items => {
this.data = [
{
name: 'Number of items',
data: items.data.map(r => r.nbr_of_items)
}
];
// HERE IS WHERE I AM POPULATING CATEGORIES
this.options.xaxis.categories = items.data.map(r => r.labels)
},
error => {
console.log(error);
}
);
When I click on the button which triggers this method, the chart shows expected data but the categories are labeled -> 1, 2, 3, 4, 5, ....
even tho when I print out this variable right in the component
{{options.xaxis.categories}}
before I hit the button, this is an empty array (which is ok - not initialized yet) and after I hit the button it shows the correct values ('A', 'B', 'C', 'D', ...)
however the chart still shows just a sequenced numbers :(
Any idea?
EDIT#1:
I tried this instead ..
// HERE IS WHERE I AM POPULATING CATEGORIES
const catgrs = items.data.map(r => r.labels)
VueApexCharts.updateOptions({
xaxis: {
categories: catgrs
}
})
Even this ..
// HERE IS WHERE I AM POPULATING CATEGORIES
const catgrs = items.data.map(r => r.labels)
VueApexCharts.updateOptions({
options: {
xaxis: {
categories: catgrs
}
}
})
no luck :(
EDIT#2: working solution ...
<template>
<v-container>
<v-btn #click="generateReport()">Generate</v-btn>
<apexchart width="75%" height="500px" class="d-flex justify-center" :options="options" :series="data"></apexchart>
</v-container>
</template>
<script>
import VueApexCharts from 'vue-apexcharts';
import DataService from './../data.service';
export default {
name: 'Data',
components: { apexchart: VueApexCharts },
data: () => ({
options: {
chart: { type: 'line', zoom: { enabled: false } },
xaxis: {
categories: []
},
title: {
text: 'Data report',
align: 'left'
}
},
data: []
}),
methods: {
generateReport() {
DataService.getData().then(
items => {
this.data = [
{
name: 'Number of items',
data: items.data.map(r => r.nbr_of_items)
}
];
// HERE IS WHERE I AM POPULATING CATEGORIES
const categories = items.data.map(r => r.labels)
this.updateAxis(categories);
},
error => {
console.log(error);
}
);
},
updateAxis(data) {
this.options = {
...this.options,
xaxis: {
categories: data
}
};
}
}
};
</script>
credits to #OscarSchafer
i'm trying to pass props from api call to a child a component but its not working atm, when i console log the data in my child it return a proxy object with the correct data. but i'm unable to use it inside the defaultData computed method in my child component where i want to used it
parent component
<template>
<CRow>
<CCol :md="6" class="mb-4">
<CCard>
<CCardHeader>Grafik Pie Hama</CCardHeader>
<CCardBody><CChartPieExample :labels="labels"/></CCardBody>
</CCard>
</CCol>
</CRow>
</template>
<script>
import * as Charts from './index.js'
import axios from 'axios'
export default {
name: 'Charts',
components: {
...Charts,
},
data() {
return {
labels: [],
}
},
methods: {
async getData() {
let formdata = new FormData();
formdata.append("work_location", "1");
formdata.append("date_from", "2020-01-01");
formdata.append("date_to", "2021-12-28");
formdata.append("id_customer", "3");
formdata.append("id_customer_location", "0");
const headers = {
'Authorization': '1cf34c57882bf600d69d9828ee639232KVpR0'
}
try {
await axios.post("https://dev.cleancity.id/api/home/ListResultReportBinatang", formdata, {headers: headers}).then(res => {
res.data.response.data.map((item) => {
this.labels.push(item.name_animal)
})
});
} catch (error) {
console.log(error)
}
},
},
created() {
this.getData()
},
}
</script>
child component
<template>
<CChartPie :data="defaultData"/>
</template>
<script>
import { CChartPie } from '#coreui/vue-chartjs'
export default {
name: 'CChartPieExample',
components: { CChartPie },
props: ['labels'],
computed: {
defaultData() {
return {
labels: this.labels,
datasets: [
{
backgroundColor: ['#41B883', '#E46651', '#00D8FF', '#DD1B16'],
data: [40, 20, 80, 10],
},
],
}
},
},
created() {
// this return proxy object, but unable to use this above in defaultData
console.log(this.labels)
},
}
</script>
UPDATE: i've tried using the watch method to watch for change for my labels, now the labels showed up on initial load but after i refresh the page / move to another page the labels dissapeared
updated child component
<template>
<CChartPie :data="defaultData"/>
</template>
<script>
import { CChartPie } from '#coreui/vue-chartjs'
export default {
name: 'CChartPieExample',
components: { CChartPie },
props: ['labels', 'values'],
data() {
return {
labelsLocal: this.labels,
};
},
watch: {
labels(val, oldVal) {
console.log(val)
if (val !== oldVal) this.labelsLocal = val
}
},
computed: {
defaultData() {
return {
labels: this.labelsLocal,
datasets: [
{
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850", "#734222", "#A52019", "#8A9597", "#DE4C8A", "#F44611", "#999950", "#C51D34", "#382C1E", "#CAC4B0", "#A2231D"],
data: [40, 20, 80, 10, 10],
},
],
}
},
},
mounted(){
console.log(this.labels)
},
}
</script>
You can use watch feature to update the default data.
export default {
data(){
return {
defaultData: {
}
}
}
watch: {
labels(newVal, oldVal) {
this.defaultData = {
labels: this.labels,
datasets: [...]
}
}
}
}
Did you tried to use vm.$watch API?
parent component:
data() {
return {
labels: [],
}
},
methods: {
async getData() {
const payload = this.toFormData({
work_location: '1',
date_from: "2020-01-01",
date_to: "2021-12-28",
id_customer: "3",
id_customer_location: "0"
})
const headers = {
'Authorization': '1cf34c57882bf600d69d9828ee639232KVpR0'
}
try {
await axios.post("https://dev.cleancity.id/api/home/ListResultReportBinatang", payload, {headers: headers}).then(res => {
this.labels = res.data.response.data.map(item => item.name_animal)
});
} catch (error) {
console.log(error)
}
},
toFormData(rawParams) {
const params = new FormData()
if (!this.isObject(rawParams)) return params
const keys = Object.keys(rawParams)
keys.forEach(key => {
rawParams[key] !== undefined && params.append(key, rawParams[key])
})
return params
},
isObject(obj) {
return obj !== null && typeof obj === 'object'
}
},
mounted() {
this.getData()
},
child component
data() {
return {
defaultData: {
labels: [],
datasets: [
{
backgroundColor: ["#3e95cd", "#8e5ea2","#3cba9f","#e8c3b9","#c45850", "#734222", "#A52019", "#8A9597", "#DE4C8A", "#F44611", "#999950", "#C51D34", "#382C1E", "#CAC4B0", "#A2231D"],
data: [40, 20, 80, 10, 10],
},
],
},
};
},
mounted() {
const unwatch = this.$watch('labels', function(val) {
this.$set(this.defaultData, 'labels', val)
unwatch()
})
},
I am currently making a reports page and currently struggling how to render the dataset to my BarChart. I have no problems showing static data to the chart but when I use axios it does not work. I read solutions about using watchers and mounted. But I am confused how to apply it if my BarChart is in another component.
This is my BarChart Code:
import { Bar } from "vue-chartjs";
export default {
name: "BarChart",
extends: Bar,
data() {
return {};
},
props: {
label: {
type: Array,
},
chartData: {
type: Array,
},
options: {
type: Object,
},
},
mounted() {
const dates = this.chartData.map((d) => d.date);
const totalCheckIn = this.chartData.map((d) => d.totalCheckIn);
const totalCheckOut = this.chartData.map((d) => d.totalCheckout);
this.renderChart(
{
labels: dates,
datasets: [
{
label: this.label[0],
data: totalCheckIn,
},
{
label: this.label[1],
data: totalCheckOut,
},
],
},
this.options
);
},
};
In my reports component this is how I used it:
<BarChart
v-bind:chartData="checkIn"
v-bind:options="checkInOptions"
v-bind:label="checkInLabel"
></BarChart>
import BarChart from "../components/BarChart";
export default {
name: "Reports",
components: { BarChart },
data() {
return {
checkInOptions: {
responsive: true,
maintainAspectRatio: false,
},
checkIn: [
{ date: "1", totalCheckIn: "2", totalCheckout: "2" },
{ date: "2", totalCheckIn: "1", totalCheckout: "2" },
],
checkInLabel: ["Check In", "CheckOut"],
}
},
beforeMount() {
axios
.get('http://localhost:3000/monthly-checkin/'+this.month+'/'+this.year+'')
.then((res) => {
this.checkIn = res.data.monthly;
console.log(this.checkIn)
})
.catch((err) => {
console.log(err.response.data.message);
});
}
}
Please help
Use watch inside your BarChart component as below:
watch:{
chartData:function(newVal,OldVal){
//assign chart data
},
},
Afterwards you need to execute the method where your bar chart data could be updated. Below will be the full snippet.
import { Bar } from "vue-chartjs";
export default {
name: "BarChart",
extends: Bar,
data() {
return {};
},
props: {
label: {
type: Array,
},
chartData: {
type: Array,
},
options: {
type: Object,
},
},
watch: {
chartData: function (newVal, OldVal) {
this.updateChart()
},
},
mounted() {
this.updateChart()
},
methods: {
updateChart() {
const dates = this.chartData.map((d) => d.date);
const totalCheckIn = this.chartData.map((d) => d.totalCheckIn);
const totalCheckOut = this.chartData.map((d) => d.totalCheckout);
this.renderChart(
{
labels: dates,
datasets: [
{
label: this.label[0],
data: totalCheckIn,
},
{
label: this.label[1],
data: totalCheckOut,
},
],
},
this.options
);
}
}
};
I created an application using the covid19 api. I store state and function in the context api. I want to create an apexchart, but the dailydata map that keeps it in the same place returns an empty array. I need to enter my options data with dailyDate
const {country} = useContext(countryContext);
const [dailyData,setDailyData] = useState([]);
useEffect(() => {
const fetchdata = async () => {
const data = await dayOneAllStatus(country);
setDailyData(data);
};
fetchdata();
},[country])
const [options,setOptions] = useState({
chart: {
height: 350,
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
xaxis: {
type: 'datetime',
categories: dailyData.map(item => item.Date)
},
tooltip: {
x: {
format: 'dd/MM/yy'
},
},
});
const [series,setSeries] = useState([
{
name: 'Vaka',
data: dailyData.map(item => item.Confirmed)
}, {
name: 'İyileşen',
data: dailyData.map(item => item.Recovered)
},
{
name: 'Ölüm',
data: dailyData.map(item => item.Deaths)
}
]);
return (
<div id="chart">
<Chart options={options} series={series} type="area" height={350} />
</div>
)
dailyData console.log example
Array(426) [ {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, … ]
[0…99]
[100…199]
[200…299]
[300…399]
[400…425]
length: 426
When you call useState, the parameter that's passed in is the initial-value. So when the function re-runs because dailyData has updated, the values in options and series don't update.
If you're not actually using setSeries and setOptions anywhere, you're better off leaving them as normal variables that are computed using dailyData:
const {country} = useContext(countryContext);
const [dailyData,setDailyData] = useState([]);
useEffect(() => {
const fetchdata = async () => {
const data = await dayOneAllStatus(country);
setDailyData(data);
};
fetchdata();
},[country])
const options = {
chart: {
height: 350,
type: 'area'
},
dataLabels: {
enabled: false
},
stroke: {
curve: 'smooth'
},
xaxis: {
type: 'datetime',
categories: dailyData.map(item => item.Date)
},
tooltip: {
x: {
format: 'dd/MM/yy'
},
},
};
const series = [
{
name: 'Vaka',
data: dailyData.map(item => item.Confirmed)
}, {
name: 'İyileşen',
data: dailyData.map(item => item.Recovered)
},
{
name: 'Ölüm',
data: dailyData.map(item => item.Deaths)
}
];
return (
<div id="chart">
<Chart options={options} series={series} type="area" height={350} />
</div>
)
In my react app, I'm getting results from pouchDB that I want to use as the data points in my series for apexCharts.
I'm getting the results and putting them in state, called maxCalories, and when logging in the console they are in this format:
So I want those 7 numbers (all with the index name of caloriesBurned to be my data in the series for the chart but I'm currently getting NaN on the graph.
Here's the full code, how can I set these to the correct format to use them in the chart data?
import React, { Component } from "react";
import Chart from "react-apexcharts";
import DB from '../../db';
import * as moment from 'moment';
class TrendsComponent extends Component {
constructor(props) {
super(props);
this.state = {
maxCalories: '',
calorieRecord: {
caloriesConsumed: '',
caloriesBurned: '',
createdAt: this.newestDate,
updatedAt: undefined
},
caloriesDB: new DB('calorie-records'),
calories: {},
calorieElements: null,
options: {
},
chart: {
toolbar: {
show:false
},
id: "basic-bar"
},
xaxis: {
categories: ['3/20', '3/21', '3/22', '3/23', '3/24', '3/25','3/26']
}
},
series: [
{
name: "Trend (tracked)",
data: {this.maxCalories}
}
]
};
}
componentDidMount(){
this.setMax();
}
setMax = () => {
this.state.caloriesDB.db.find({
selector: {
$and: [
{_id: {"$gte": null}},
{caloriesBurned: {$exists: true}},
{createdAt: {$exists: true}}
]
},
fields: ['caloriesBurned', 'createdAt'],
sort: [{'_id':'desc'}],
limit: 7
}).then(result => {
console.log('max');
console.log(result);
const newDocs = result.docs;
this.setState({
maxCalories: newDocs.map(docs => docs)
});
console.log('maxCalories');
console.log(this.state.maxCalories);
}).catch((err) =>{
console.log(err);
});
}
render() {
return (
<div className="mixed-chart">
<Chart
options={this.state.options}
series={this.state.series}
type="area"
stacked="true"
width="700"
/>
</div>
);
}
}
export default TrendsComponent;
I had the same problem in my project. And I spent a lot of time in looking for solution. So here what I get:
const FinacialResultChart = (props) => {
const options = {
chart: {
toolbar: {
show: false
},
animations: {
enabled: false
}
},
stroke: {
curve: "smooth",
dashArray: [0, 8],
width: [4, 2]
},
grid: {
borderColor: props.labelColor
},
legend: {
show: false
},
colors: [props.dangerLight, props.strokeColor],
fill: {
type: "gradient",
gradient: {
shade: "dark",
inverseColors: false,
gradientToColors: [props.primary, props.strokeColor],
shadeIntensity: 1,
type: "horizontal",
opacityFrom: 1,
opacityTo: 1,
stops: [0, 100, 100, 100]
}
},
markers: {
size: 0,
hover: {
size: 5
}
},
xaxis: {
labels: {
style: {
colors: props.strokeColor
}
},
axisTicks: {
show: false
},
categories: [
"Январь",
"Февраль",
"Март",
"Апрель",
"Май",
"Июнь",
"Июль",
"Август",
"Сентябрь",
"Октябрь",
"Ноябрь",
"Декабрь"
],
axisBorder: {
show: false
},
tickPlacement: "on"
},
yaxis: {
tickAmount: 5,
labels: {
style: {
color: props.strokeColor
}
}
},
tooltip: {
x: { show: false }
}
}
const data = [
{
name: "Итоговый результат",
data: props.userData.traidingMonth
}
]
return (
<Chart
options={options}
series={data}
type="line"
height={280}
/>
)
}
export default FinacialResultChart
So you need to change your class to const, and push all your props (api data for example) into your children chart component. In chart options you can get the chart data with props.data