Related
I am having an issues where my data points from data retrieved from firestore are not correctly positioned for the y-axis though the tooltip data is correct. For example, the data below is suppose to be at 23 but its positioned at 100+.
When I manually type in the data, it is positioned correctly so I thought that it might be an issue with the data type after retrieval from the database but I checked and it is appropriately a 'number' data type.
I am not so sure how this issue came about and how to rectify it. I would greatly appreciate your help on this problem! Thanks!
<script>
import {
auth,
db
} from "../../src/main";
import Sidebar from "../components/Navigation/Sidebar.vue";
import Topbar from "../components/Navigation/Topbar.vue";
import ChartTest from "../components/ProgressPage/ChartTest.vue";
import Chart from 'chart.js/auto';
import {
getFirestore,
doc,
updateDoc,
getDoc,
setDoc,
collection,
addDoc,
deleteDoc,
deleteField,
arrayUnion,
arrayRemove
} from "firebase/firestore";
export default {
name: "Progress",
components: {
Sidebar,
Topbar,
ChartTest
},
mounted() {
const progressChart = new Chart(document.getElementById("progress-chart"), {
type: 'line',
data: {
labels: ['CA1', 'SA1', 'CA2', 'SA2'],
datasets: [
]
},
options: {
plugins: {
title: {
display: true,
text: this.title
}
},
scales: {
y: {
display: true,
stacked: true,
// max: 0,
// min: 200,
title: {
display: true,
text: 'Your Score (%)'
}
}
}
}
});
this.getData().then((data) => {
progressChart.data.datasets=data
console.log(typeof(data[0].data[0]))//number data type
this.getCount(data)
progressChart.update() //updating graph with new set of data her
})
},
methods: {
getCount(data) {
this.count= data.length
},
async getData() {
var email = localStorage.getItem("email");
var ref = doc(db, 'users', email);
const docSnap = await getDoc(ref)
if (docSnap.exists()) {
var data = docSnap.data().progressResults
return data
} else {
console.log('does not exist')
}
},
async addResult() {
let count = this.existingSubjects.length
var email = localStorage.getItem("email");
var ref = doc(db, 'users', email);
if (!this.existingSubjects.includes(this.subject)) {
this.existingSubjects.push(this.subject)
const newData = {
data: [{ x: this.examType,y:this.score}],
label: this.subject,
borderColor: this.colors[this.count],
fill: false
}
await updateDoc(
ref, {
progressResults: arrayUnion(newData)
}
)
console.log(newData)
} else {
//TBC
}
}
},
data() {
return {
score: '',
examType: '',
subject: '',
count:0,
existingSubjects: [],
colors: ["#3e95cd", "#8e5ea2", "#3cba9f", "#e8c3b9", "#c45850","#21c095","#bbc021","#1a993a","##904b23","#a01359","#a04913","#534270"],
title: '',
data: {
labels: ['CA1', 'SA1', 'CA2', 'SA2'],
datasets: [
]
},
tabs: [{
link: '',
name: "subject",
dropdown: true,
dropdownTabs: [{
name: 'math',
link: '#'
},
{
name: 'science',
link: '#'
},
]
},
{
link: '#',
name: "test",
dropdown: false,
dropdownTabs: []
},
]
}
}
}
</script>
This is because you set your y axis to stacked so it puts all values on top of each other where the space between the lines is the value you provide.
To get the behaviour you want you need to remove the stacked or set it to false:
y: {
stacked: false
}
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)
}, []);
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()
})
},
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
I'm using apex charts for react in my reactJS progressive web app, and have had no issues with it for static data, but I"m now trying to take a returned array from my database and use the array for the graph and it's not working.
I'm logging the array returned from the database, which is in this structure:
And in my code, I'm setting this to the third series option of my chart named "Trends" but when the graph loads the line for that data is 'Nan'
What am I doing wrong here?
class TrendsComponent extends Component {
constructor(props) {
super(props);
this.state = {
maxCalories: '',
calorieRecord: {
caloriesConsumed: '',
caloriesBurned: '',
createdAt: undefined,
updatedAt: undefined
},
options: {
fill: {
colors: ['#FF756D', '#85DE77', '#FFF49C']
},
dataLabels: {
enabled: true,
textAnchor: 'middle',
distributed: false,
offsetX: 0,
offsetY: 0,
style: {
fontSize: '14px',
fontFamily: 'Helvetica, Arial, sans-serif',
fontWeight: 'bold',
colors: ["#FF756D", "#85DE77", "#FFF49C"]
},
background: {
enabled: true,
foreColor: '#fff',
padding: 4,
borderRadius: 2,
borderWidth: 1,
borderColor: '#fff',
opacity: 0.9,
},
dropShadow: {
enabled: true,
top: 1,
left: 1,
blur: 1,
color: '#000',
opacity: 0.8
}
},
colors: ["#FF756D", "#85DE77", "#FFF49C"],
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: "Baseline",
data: [250,500,234,389,644,245,590]
},
{
name: "Optimal",
data: [2250,2250,2250,2250,2250,2250,2250]
},
{
//this is the line where I'm getting NaN
name: "Trend (tracked)",
data: [this.maxCalories]
}
]
};
}
...
UPDATE:
Upon component mounting, I use these functions to set MaxCalories which is the data array I'm using for the chart
getMax = () => {
this.state.caloriesDB.db.createIndex({
index: {
fields: ['_id','caloriesBurned']
}
}).then(result => {
console.log(result);
this.setMax();
}).catch((err) =>{
console.log(err);
});
}
setMax = () => {
this.state.caloriesDB.db.find({
selector: {
$and: [
{_id: {"$gte": null}},
{caloriesBurned: {$exists: true}},
]
},
fields: ['caloriesBurned'],
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);
});
}