How to Render Chart Datasets in Vue? - javascript

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
);
}
}
};

Related

Data point in Chart js line chart not positioned along y axis correctly when retrieve data from firestore

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
}

useEffect not printing div element react hooks

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)
}, []);

ApexCharts dynamically change xaxis.categories

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

CoreUI Vue chart pass async props to child not working

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()
})
},

How to annotate any data point in ant design timeline graph?

I want to show a line like above in ant design timeline graph, ( Line Chart ). It's a dual Axes graph. I am using React and Ant Design. I want to show the line at any data point. I think I have to use annotations, but I am not getting how.
Data Structure is like below
[
{
"date": "10/09/2020",
"type": "Financial impact",
"count": 2180
},
.....
]
Here's my below Code for reference
import React from "react";
import { DualAxes } from "#ant-design/charts";
import { Row, Typography, Col } from "antd";
import { Badge } from "antd";
import NumberFormat from "react-number-format";
const TimelineChart = ({ data }: { data: any[] }) => {
var config: any = {
xField: "date",
yField: ["count", "value"],
data: data,
xAxis: {
label: {
style: {
fill: "#6E759F",
},
},
},
yAxis: {
count: {
label: {
style: {
fill: "#6E759F",
},
},
},
value: {
label: {
style: {
fill: "#03838B",
},
},
},
},
geometryOptions: [
{
geometry: "line",
color: ["#2F54EB", "#9254DE"],
seriesField: "type",
},
{
geometry: "line",
color: "#2AABAB",
seriesField: "type",
},
],
legend: false,
formatter: (data: any) => {
return {
...data,
};
},
},
};
return <DualAxes {...config} />;
};
export default TimelineChart;

Categories

Resources