My problem: I followed the doc from vue-chartjs but the chart is not rendering the data.
Here is my code:
My component Chart.vue
<script>
import { Line } from 'vue3-chart-v2'
export default {
extends: Line,
props: {
chartdata: {
type: Object,
default: null
},
options: {
type: Object,
default: null
}
},
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
</script>
My Home.vue code:
<template>
<div class="container">
<line-chart
v-if="loaded"
:chartdata="chartdata"
:options="options"/>
</div>
</template>
<script>
import LineChart from '../components/Chart.vue'
export default {
name: 'LineChartContainer',
components: { LineChart },
data: () => ({
loaded: false,
chartdata: null,
options: {
responsive: true,
}
}),
async mounted () {
this.loaded = false
try {
const quotes = [
1, 2, 3, 4,
]
this.chartdata = quotes
this.loaded = true
} catch (e) {
console.error(e)
}
}
}
</script>
When I'm rendering the Home view, I see the axis of the chart, but no data inside...
In the console, I don't have any errors.
Later, I would like to fetch data from an API. Thus, the quotes would receive data from an external API. Here I just put some fake data for testing.
Does anyone can help me ? Thks
I was missing the import { defineComponent } from 'vue' in my component. Now it works using the below code.
<template>
<div class="container">
<line-chart
v-if="loaded"
:chartdata="chartdata"
:options="options"/>
</div>
</template>
<script>
import LineChart from '../components/Chart.vue'
import { defineComponent } from 'vue'
export default defineComponent({
name: 'LineChartContainer',
components: { LineChart },
data: () => ({
loaded: false,
chartdata: null,
options: {
responsive: true,
}
}),
async mounted () {
this.loaded = false
try {
const quotes = [
1, 2, 3, 4,
]
this.chartdata = quotes
this.loaded = true
} catch (e) {
console.error(e)
}
}
})
</script>
Related
I trying to make a COVID19 visualization site using Chart.js and VueJs
this is My App.vue that contains the API call and stores the data into arrays
<template>
<div id="app" class="container">
<div class="row mt-5" v-if="PositiveCases.length > 0">
<div class="col">
<h2>Positives</h2>
<lineChart :chartData="PositiveCases" :options="chartOptions" label="Positive" />
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import moment from 'moment'
import lineChart from "./components/lineChart.vue";
export default {
name: 'App',
components: {
lineChart
},
data(){
return{
PositiveCases : [],
Deaths: [],
Recoverd: [],
chartOptions: {
responsive: true,
maintainAspectRatio: false
}
}
},
async created(){
const {data} = await axios.get('https://api.covid19api.com/live/country/egypt')
//console.log(data);
data.forEach(d => {
const date = moment(d.Date,"YYYYMMDD").format("MM/DD")
const {Confirmed,Deaths,Recovered} = d
this.PositiveCases.push({date, total : Confirmed})
this.Deaths.push({date, total : Deaths})
this.Recoverd.push({date, total : Recovered})
// console.log("PositiveCases",this.PositiveCases);
// console.log("Deaths",this.Deaths);
// console.log("Recoverd",this.Recoverd);
});
}
}
</script>
and this is my lineChart.vue that contains the Line chart code the data stored correctly in both the dates and totals
<script>
import {Line} from 'vue-chartjs'
export default {
extends: Line,
props: {
label:{
type: String,
},
chartData:{
type: Array,
},
options:{
type: Object,
}
},
mounted(){
const dates = this.chartData.map(d => d.date).reverse()
const totals = this.chartData.map(d => d.total).reverse()
console.log("dates",dates);
console.log("totals",totals);
this.renderChart({
labels: dates,
datasets: [{
label: this.label,
data: totals,
}],
},this.options
)
}
}
</script>
the error in the console says
want to know what is the solution, all the data are stored correctly in both files
You are using V4 of vue-chart.js, the chart creation process has been changed as you can read here in the migration guide.
So instead of calling this.renderChart which was the old syntax you now have to use the actual component and pass the data to it like so:
<template>
<Bar :chart-data="chartData" />
</template>
<script>
// DataPage.vue
import { Bar } from 'vue-chartjs'
import { Chart, Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale } from 'chart.js'
Chart.register(Title, Tooltip, Legend, BarElement, CategoryScale, LinearScale)
export default {
name: 'BarChart',
components: { Bar },
data() {
return {
chartData: {
labels: [ 'January', 'February', 'March'],
datasets: [
{
label: 'Data One',
backgroundColor: '#f87979',
data: [40, 20, 12]
}
]
}
}
}
}
</script>
So I have added a second bus to my code that runs on create, but no matter in which order I call the Busses the second bus (eventBus2) is never called and then returns no data. By printing some console logs I get the feeling that that eventBus2.$on is never executed. Is there some Vue rule that I'm not aware of, any suggestions?
Item.vue
<template>
<div>
<table>
<tr
v-for="item in info"
:key="item.id"
#click="editThisItem(item.id)"
>
<td>{{ item.name}}</td>
<td>{{ item.number}}</td>
<td>{{ item.size}}</td>
</tr>
</table>
</div>
</template>
<script>
import Something from "./Something.vue";
import axios from "axios";
import { eventBus } from "../main";
import { eventBus2 } from "../main";
export default {
components: { Something },
name: "Item",
data() {
return {
selected_item_id: 0,
info: null,
};
},
methods: {
editThisItem(bolt) {
this.selected_item_id = bolt;
eventBus2.$emit("itemWasSelected", this.selected_item_id);
eventBus.$emit("newTabWasAdded", "edit-item");
},
},
mounted() {
axios
.get("http://localhost:8080/items")
.then((response) => (this.info = response.data._embedded.artikli));
},
};
</script>
EditItem.vue
<script>
import Something from "./Something.vue";
import axios from "axios";
import { eventBus2 } from "../main";
export default {
components: { Something},
name: "Edit-item",
data() {
return {
info: null,
select_number: 0,
select_name: "",
selected_item_id: -1,
priv_item: {
id: 0,
size: "big"
},
};
},
mounted() {
if (this.selected_item_id != -1) {
axios
.get("http://localhost:8080/items/" + this.selected_item_id)
.then((response) => (this.priv_item = response.data));
}
},
created() {
eventBus2.$on("itemWasSelected", (data) => {
this.selected_item_id = data;
console.log(" + " + data);
//this console log does not even print the "+", the data is empty
});
console.log(this.selected_item_id);
},
};
</script>
main.js
export const eventBus = new Vue();
export const eventBus2 = new Vue();
you're expecting itemWasSelected and emitting WasSelected they should be the same.
PD: that can be done in one line.
import { eventBus } from "../main";
import { eventBus2 } from "../main";
import { eventBus, eventBus2 } from "../main";
working on a covid app to get familiar with vue2js.
Am now trying to get a graph with vue-chartjs but am failing to pass the data to the graph/chart component.
I make an API request with vuex and passing the data to my component: CountryGraph.vue which contains a Graph.vue with the chart itself.
vuex -> CountryGraph.vue -> Graph.vue
Passing data into CountryGraph.vue works:
But when I try to pass my data (countryGraph) as props to my char/Graph.vue component, then it is not done and I get in Graph.vue only the value undefined:
Why?
Below my code, first the CountryGraph.vue:
<template>
<section class="countryGraph">
<LineChart
:chartdata="chartData"
:options="chartOptions"
/>
</section>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import LineChart from "../graph/Graph";
export default {
name: "CountryGraph",
components: { LineChart },
data: () => ({
chartData: {
labels: this.countryGraph.map((el) => el.date),
datasets: [
{
label: "Confirmed",
backgroundColor: "#f87979",
data: this.countryGraph.map(
(el) => el.confirmed
),
},
],
},
chartOptions: {
responsive: true,
maintainAspectRatio: false,
},
}),
methods: {
...mapActions(["selectCountryGraph"]),
},
computed: {
...mapGetters(["countryGraph"]),
},
};
</script>
<style></style>
And my chart/Graph.vue component which is made so, that I can reuse it (as stated in vue-chartjs guide):
<script>
import { Bar } from "vue-chartjs";
export default {
extends: Bar,
props: {
chartdata: {
type: Object,
default: null,
},
options: {
type: Object,
default: null,
},
},
mounted() {
this.renderChart(this.chartdata, this.options);
},
};
</script>
<style />
When I use mocked data, like instead of
labels: this.countryGraph.map((el) => el.data)
I do labels: ["q", "w", "e", "r", "t"]
and instead of
data: this.countryGraph.map(el => el.confirmed)
I do data: [0, 1, 2, 3, 4]
everything works fine.
Also, when I pass my variables directly into the component, like:
<LineChart
:chartdata="this.countryGraph.map((el) => el.data)"
:options="chartOptions"
/>
Then I can see the data as props in the child (Graph.vue) component.
But in this case I use v-bind: and in the earlier one not. Maybe that is the problem?
A couple issues to note:
It looks like you're mapping a nonexisting property (el.data should be el.date). Possibly just a typo in the question.
this.countryGraph.map((el) => el.data) ❌
^
data() is not reactive, and cannot rely on computed props, so the countryGraph computed prop will not be available in data() and will not update chartData with changes. One way to fix this is to make chartData a computed prop:
export default {
computed: {
...mapGetters(["countryGraph"]),
// don't use an arrow function here, as we need access to component instance (i.e., this.countryGraph)
chartData() {
return {
labels: this.countryGraph.map((el) => el.date),
datasets: [
{
label: "Confirmed",
backgroundColor: "#f87979",
data: this.countryGraph.map((el) => el.confirmed),
},
],
}
}
}
}
I have created a doughnut chart using vue-chart.js, Chart.js and using some values which are within my vuex state. The chart works apart from when the vuex state is updated the chart doesn't updated too.
I have tried to use computed properties to try keep the chart up to date. But then I get the following errors:
Error in callback for watcher "chartData": "TypeError: Cannot set property 'data' of undefined"
TypeError: Cannot set property 'data' of undefined
DoughnutChart.vue:
<script>
import { Doughnut, mixins } from 'vue-chartjs';
const { reactiveProp } = mixins;
export default {
extends: Doughnut,
mixins: [reactiveProp],
props: ['chartData', 'options'],
mounted () {
this.renderChart(this.chartdata, this.options)
}
}
</script>
AppCharts.vue:
<template>
<div id="chart_section">
<h2 class="section_heading">Charts</h2>
<div id="charts">
<DoughnutChart :chart-data="datacollection" :options="chartOptions" class="chart"></DoughnutChart>
</div>
</div>
</template>
<script>
import DoughnutChart from './DoughnutChart';
import { mapGetters } from 'vuex';
export default {
components: {
DoughnutChart
},
computed: {
...mapGetters(['boardColumnData']),
datacollection() {
return {
datasets: [{
data: [this.boardColumnData[0].total.$numberDecimal, this.boardColumnData[1].total.$numberDecimal, this.boardColumnData[2].total.$numberDecimal, this.boardColumnData[3].total.$numberDecimal],
backgroundColor: [
'#83dd1a',
'#d5d814',
'#fdab2f',
'#1ad4dd'
],
borderColor: [
'#83dd1a',
'#d5d814',
'#fdab2f',
'#1ad4dd'
],
}]
}
},
},
data() {
return {
chartOptions: null
}
},
mounted () {
this.fillData();
},
methods: {
fillData() {
this.chartOptions = {
responsive: true,
maintainAspectRatio: false
}
}
}
}
</script>
this is what boardColumnData looks like after getting the state:
[
{
"name":"Opportunities",
"percentage":{
"$numberDecimal":"0"
},
"total":{
"$numberDecimal":70269
}
},
{
"name":"Prospects",
"percentage":{
"$numberDecimal":"0.25"
},
"total":{
"$numberDecimal":0
}
},
{
"name":"Proposals",
"percentage":{
"$numberDecimal":"0.5"
},
"total":{
"$numberDecimal":5376
}
},
{
"name":"Presentations",
"percentage":{
"$numberDecimal":"0.75"
},
"total":{
"$numberDecimal":21480
}
},
]
The $numberDecimal value is what is updated inside vuex when an event on another component happens. When these values are changed I want the chart to update with the new values.
You must pass the data in the prop. I.e. fastest solution for you would be to have fillData() return the datacollection in the correct format.
E.g.
fillData(){
return {
datasets: [
{
data: [
this.boardColumnData[0].total.$numberDecimal,
this.boardColumnData[1].total.$numberDecimal,
this.boardColumnData[2].total.$numberDecimal,
this.boardColumnData[3].total.$numberDecimal,
],
backgroundColor: ["#83dd1a", "#d5d814", "#fdab2f", "#1ad4dd"],
borderColor: ["#83dd1a", "#d5d814", "#fdab2f", "#1ad4dd"],
},
];
}
}
You need to do the same for the options, but pass them in a new options=yourOptions() prop.
This drove me absolutely bonkers. I hope this still helps you (or someone else).
I want to display data from API with highcharts but still get an error because highchart config declared outside the class app and data fetched inside class app.
It works if use static data,
here is the example from static data link.
import React, { Component } from 'react';
import { render } from 'react-dom';
import ReactHighstock from 'react-highcharts/ReactHighstock';
import './style.css';
const data = [
[1220832000000, 22.56], [1220918400000, 21.67], [1221004800000,
21.66], [1221091200000, 21.81], [1221177600000, 21.28],
[1221436800000, 20.05], [1221523200000, 19.98], [1221609600000,
18.26], [1221696000000, 19.16], [1221782400000, 20.13],
[1222041600000, 18.72], [1222128000000, 18.12], [1222214400000,
18.39], [1222300800000, 18.85], [1222387200000, 18.32],
[1222646400000, 15.04], [1222732800000, 16.24], [1222819200000,
15.59], [1222905600000, 14.3], [1222992000000, 13.87],
[1223251200000, 14.02], [1223337600000, 12.74], [1223424000000,
12.83], [1223510400000, 12.68], [1223596800000, 13.8],
[1223856000000, 15.75], [1223942400000, 14.87], [1224028800000,
13.99], [1224115200000, 14.56], [1224201600000, 13.91],
[1224460800000, 14.06], [1224547200000, 13.07], [1224633600000,
13.84], [1224720000000, 14.03], [1224806400000, 13.77],
[1225065600000, 13.16], [1225152000000, 14.27], [1225238400000,
14.94], [1225324800000, 15.86], [1225411200000, 15.37],
[1225670400000, 15.28], [1225756800000, 15.86], [1225843200000,
14.76], [1225929600000, 14.16], [1226016000000, 14.03],
[1226275200000, 13.7], [1226361600000, 13.54], [1226448000000,
12.87], [1226534400000, 13.78], [1226620800000, 12.89],
[1226880000000, 12.59], [1226966400000, 12.84], [1227052800000,
12.33], [1227139200000, 11.5], [1227225600000, 11.8],
[1227484800000, 13.28], [1227571200000, 12.97], [1227657600000,
13.57], [1227830400000, 13.24], [1228089600000, 12.7],
[1228176000000, 13.21], [1228262400000, 13.7], [1228348800000,
13.06], [1228435200000, 13.43], [1228694400000, 14.25],
[1228780800000, 14.29], [1228867200000, 14.03], [1228953600000,
13.57], [1229040000000, 14.04], [1229299200000, 13.54]
];
const config = {
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
};
class App extends Component {
render() {
return (
<div className="App" />
);
}
}
render(<ReactHighstock config={config}/>,
document.getElementById('root'));
With example above, two variable data and config placed outside the class app, so i think it's not problem.
But if i want to fetch data from API, the data placed inside the class app and config still outside class app.
So i get an error 'data is not defined'
here is the link
import React, { Component } from 'react';
import { render } from 'react-dom';
import ReactHighstock from 'react-highcharts/ReactHighstock';
import './style.css';
const config = {
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
};
class App extends Component {
constructor() {
super();
this.state = {
dataResults: [],
}
}
componentDidMount() {
this.getData();
}
getData = () => {
fetch('https://cdn.rawgit.com/highcharts/highcharts/057b672172ccc6c08fe7dbb27fc17ebca3f5b770/samples/data/usdeur.json')
.then(res => res.json())
.then(data => {
this.setState({
dataResults: data
});
});
}
render() {
return (
<div className="App" />
);
}
}
render(<ReactHighstock config={config}/>,
document.getElementById('root'));
How to use highcharts with fetched data from API?
Your App component is currently not part of your DOM as you're rendering ReactHighstock as a root component. One solution would be to render the chart inside the App component and pass the data dynamically:
const getConfig = data => ({
rangeSelector: {
selected: 1
},
title: {
text: 'aAAPL Stock Price'
},
series: [{
name: 'AAPL',
data: data,
tooltip: {
valueDecimals: 2
}
}]
});
class App extends Component {
constructor() {
super();
this.state = {
dataResults: [],
}
}
componentDidMount() {
this.getData();
}
getData = () => {
fetch('your_endpoint_url')
.then(res => res.json())
.then(data => {
this.setState({
dataResults: data
});
});
}
render() {
const { dataResults } = this.state;
const chartConfig = getConfig(dataResults);
return (
<ReactHighstock config={chartConfig}/>
);
}
}
render(<App/>,
document.getElementById('root'));
You should of course handle a use case where the data has not been fetched yet and display a placeholder.