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
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()
})
},
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'm new when using materialUI table in react.js, I want to try using react-material table but I got lost as how can I show my data in the table, Let say I have 28 data and in fact it already show the right number in the pagination but the data itself doesn't show anything. this is the documentation link for react-material table Check this.
I already read several topic about this but all of them using tableRow, tableHead, and etc.
this is my component code:
import React, { Component } from 'react';
import MaterialTable from 'material-table';
import { history } from '../../../../Helpers/history';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { withRouter } from 'react-router-dom';
import { orderActions } from '../../../../Actions/orderActions';
import { withStyles } from '#material-ui/core/styles';
// Component
import './tabledata.css';
const styles = theme => ({
'#global': {
body: {
backgroundColor: theme.palette.common.white,
},
},
});
class Tabledata extends Component {
constructor(props) {
super(props);
// const { orders } = this.props;
this.state = {
columns: [
{ title: 'Nama Pemesanan', field: 'name' },
{ title: 'Status', field: 'status' },
{ title: 'Estimasi Pengerjaan (Hari)', field: 'estimate', type: 'numeric' },
{ title: 'Jumlah Pesanan (pcs)', field: 'unit', type: 'numeric' },
{ title: 'Harga (Rp)', field: 'price', type: 'numeric' },
],
data: [
{
id: 2,
name: 'lala',
status: 'Penyablonan',
estimate: 8,
unit: 36,
price: '36.000.000',
},
],
}
}
componentDidMount() {
if(localStorage.getItem('auth')) {
const { dispatch } = this.props;
dispatch(orderActions.getAllOrder());
// history.push('/dashboard');
}
}
componentWillReceiveProps(newProps){
this.setState({ loading: newProps.loading }); // remove the loading progress when logged in or fail to log in
}
handleChange = prop => event => {
this.setState({ [prop]: event.target.value });
};
change(data){
console.log("Check ID : ", data);
}
render(){
const { orders } = this.props;
console.log("test console : ", orders)
return (
<div className="tabledata-order">
<div className="row item-section">
<div className="col">
<div className="card">
<div className="card-body">
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
</div>
</div>
</div>
</div>
</div>
);
}
}
Tabledata.propTypes = {
classes: PropTypes.object.isRequired
};
const mapStateToProps = (state) => {
const { orders } = state.orderPage;
return {
orders
};
}
const connectedTableDataPage = withRouter(connect(mapStateToProps, '', '', {
pure: false
}) (withStyles(styles)(Tabledata)));
export { connectedTableDataPage as Tabledata };
As you can see, this material table have a component like this
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
As you can see, in the bottom of the image you can see 1-5 of 28 and in my console there is exactly 28 data but the table itself doesn't show any data
can someone help me? how can I show the data in orders and this is the example of the image json that I have:
Finally I can fix this problem, this answer for you who have facing the same problem with react-material table if your data doesn't show but it show in console.log. you must check the field in column
this.state = {
columns: [
{ title: 'Nama Pemesanan', field: 'name' },
{ title: 'Status', field: 'status' },
{ title: 'Estimasi Pengerjaan (Hari)', field: 'estimate', type: 'numeric' },
{ title: 'Jumlah Pesanan (pcs)', field: 'unit', type: 'numeric' },
{ title: 'Harga (Rp)', field: 'price', type: 'numeric' },
],
data: [
{
id: 2,
name: 'lala',
status: 'Penyablonan',
estimate: 8,
unit: 36,
price: '36.000.000',
},
],
}
let say, json that I got have city, color, and weight then you must state the column field as such:
this.state = {
columns: [
{ title: 'detail Address', field: 'city' },
{ title: 'color', field: 'color' },
{ title: 'weight', field: 'weight' },
],
}
and for the MaterialTable you can just put all the variable you have like this
<MaterialTable
title="Daftar Pesanan"
columns={this.state.columns}
key={orders._id}
data={orders}
/>
and you can get the data like I show you below
I hope this answer can help you who have a hard time with react-material table