How to set prop data for chart.js in React? - node.js

I'm trying to fetch data from a SQL-database and show that data on a chart.js, but I'm getting this error:
Warning: Failed prop type: Invalid prop data supplied to ChartComponent.
My code looks like this:
import React, {Component} from 'react';
import {Line} from 'react-chartjs-2';
class MinData extends Component{
constructor(){
super();
this.state = {
data: {
labels: [],
datasets: []
}
};
}
componentDidMount(){
fetch('http://localhost:4000/api/myData?limit=6')
.then (results =>{
return results.json();
}).then(data => {
let receivedData = data.map((datapost) => {
return(
{
data: {
labels: datapost.timestamp,
datasets: datapost.temp_0
}
}
)
})
this.setState({data: receivedData}, function(){
console.log(this.state.data);
});
})
}
render(){
return(
<div className="enContainer">
<Line
data={this.state.data}
options={{
title:{
display: true,
text: 'Fladan mätpunkt',
fontSize: 25
}
}}
/>
</div>
)
}
}
export default MinData;
The idea is to set state of data with the fetched data.
I'm running out of ideas, but I guess there's something wrong with the way I return data from my map function.
UPDATE:
This is what I receive in Postman when doing the same request with limit set to receive two objects:
[
{
"timestamp": "2019-01-17T18:14:20.000Z",
"battery": 5.094,
"temp_0": 23.375,
"temp_10": 19.125,
"temp_20": 19,
"temp_30": 18.812,
"temp_40": 18.562,
"temp_50": 18.625,
"temp_60": 18.688,
"temp_70": 18.688,
"temp_80": 18.188,
"temp_90": 19,
"temp_100": 18.75,
"temp_110": 18.625,
"temp_120": 18.5
},
{
"timestamp": "2019-01-17T18:17:25.000Z",
"battery": 5.104,
"temp_0": 23.375,
"temp_10": 19.125,
"temp_20": 19,
"temp_30": 18.812,
"temp_40": 18.562,
"temp_50": 18.688,
"temp_60": 18.75,
"temp_70": 18.688,
"temp_80": 18.188,
"temp_90": 19,
"temp_100": 18.75,
"temp_110": 18.625,
"temp_120": 18.5
}
]

You need to check if data is present in this.state.data.labels before calling Line component. Render method would have run before componentDidMount gets a chance to return and call api therefore empty data is passed and passed to Line component.
{
this.state.data.labels.length && <Line
data={this.state.data}
options={{
title: {
display: true,
text: 'Fladan mätpunkt',
fontSize: 25
}
}}
/>
}
State data should have following structure:
{
labels: ['First', 'Second'],
datasets: [
{
label: 'My First dataset',
data: [65, 59, 80, 81, 56, 55, 40],
},
{
label: 'My Second dataset',
data: [28, 48, 40, 19, 86, 27, 90],
},
]
}

Related

Why would react-chartjs-2 not be able to see the data it needs to render?

Thanks for any tips and or help! I've hit a wall here trying to get a chart to render. I've reverted to testing a very simple approach (code below) and I am still getting the following error:
TypeError: Cannot read properties of undefined (reading 'map')
I can log the data being set from the useEffect call, but I cant understand why its not making it into the Line graph. From the debugger (on utils.ts) I can see that (currentData = {labels: undefined, datasets: Array(0)}) and nextDatasets = undefined.
I'm starting to wonder if there is some version mismatch somewhere, anyways thanks for any ideas!
import React, { useState, useEffect } from "react";
import {Line} from "react-chartjs-2";
function Graph() {
const myLabels = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
const [data, setData] = useState({});
useEffect(() => {
setData({
labels: myLabels,
datasets: [
{
label: 'The Level',
data: [21, 53, 65, 12, 32]
},
]
});
}, [])
console.log(data.datasets);
return(
<div style={{height: "500px", width: "500px"}}>
<Line data={data} />
</div>
)}
export default Graph;
The following version are in use:
"react": "^17.0.2" with
"chart.js": "^3.6.2",
"react-chartjs-2": "^4.0.0"
After some reading... I re-visited my approach and came up with the following, I'm not sure this is the proper way to go about things, still learning so if anyone has any helpful comments they'd be welcomed!
I'm not sure I completely understand at this point, but I think that the main problem I had was related to the useEffect() being used to build the data for the graph, as opposed to maybe effect the graph once it's there.
import React from "react";
import { useState } from "react";
import 'chart.js/auto';
import { Chart } from 'react-chartjs-2';
function Graph() {
const [chartData, setChartData] = useData();
return(
<div style={{height: "500px", width: "500px"}}>
<Chart type='line' data={chartData} />
</div>
)
}
export default Graph;
const useData = ( data = {} ) => {
const [state, setState] = useState(data);
data = {
labels: ['monday', 'tuesday', 'wednesday', 'thursday', 'friday'],
datasets: [
{
label: 'The Level',
data: [21, 53, 65, 12, 32]
},
]
}
return [data]
}
By default data is blank, that's why it gives an error so just you have to ensure that if data is available then you can draw the line chart.
Just change one line of code: {data.datasets ? <Line data={data} /> : ""}
import React, { useState, useEffect } from "react";
import { Line } from "react-chartjs-2";
function Graph() {
const myLabels = ["monday", "tuesday", "wednesday", "thursday", "friday"];
const [data, setData] = useState({});
useEffect(() => {
setData({
labels: myLabels,
datasets: [
{
label: "The Level",
data: [21, 53, 65, 12, 32]
}
]
});
}, []);
console.log(data.datasets);
return (
<div style={{ height: "500px", width: "500px" }}>
{data.datasets ? <Line data={data} /> : ""}
</div>
);
}
export default Graph;

How to Pass data from api into pie chart with angular

I would like to pass the output of an API in a piechart canvas with angular but I could not achieve any result, the API is consumed but I have a problem associating it with the piechart (datapoints)
the code just below.
app.component.ts
import { Component, OnInit } from '#angular/core';
import { EmailValidator } from '#angular/forms';
import { Router, NavigationEnd } from '#angular/router';
import { ApiService } from './api.service';
import { ApistatService } from './apistat.service';
import * as CanvasJS from './canvasjs.min';
//var CanvasJS = require('./canvasjs.min');
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
elements: any[];
constructor(
private apistatService: ApistatService
) {
this.elements = [];
}
ngOnInit() {
let chart = new CanvasJS.Chart("chartContainer", {
theme: "light2",
animationEnabled: true,
exportEnabled: true,
title:{
text: "Monthly Expense"
},
data: [{
type: "pie",
showInLegend: true,
toolTipContent: "<b>{elements.total}</b>: ${y} (#status)",
indexLabel: "{name} - #percent%",
dataPoints: [
{ y: 120, name: "Insurance" },
{ y: 300, name: "Traveling" },
{ y: 800, name: "Housing" },
{ y: 150, name: "Education" },
{ y: 150, name: "Shopping"},
{ y: 250, name: "Others" }
]
}]
});
this.apistatService.getData().subscribe((data:any) => {
if(data.status === 200) {
console.log(data.response);
this.elements = data.response;
}
})
chart.render();
}
}
app.component.html
<div id="chartContainer" style="height: 370px; width: 100%; margin-left:auto;margin-right:auto;">
</div>
To consume API data in pie chart you should just set data from API to dataPoints array just make sure your API data has same formatting as currently available inside dataPoints array which you are passing in data array while rendering chart, and it will work.

Data isn't pulling through from mongoDB into my chart even though I have the JSON data

I have data saved in my mongoDB which holds players names and scores, I have managed to create a score board with the data outputting (top 10) in DESC order, however for some reason when I use the name and the score for the x and y axis on my chart, chrome's console error has score is not defined?
Any guidance, this has taken me way to long trying to debug this. The score board on the left is working fine, the graph however is not...
leadership.html
<script>
$(function() {
//TESTING FOR LEADERSHIP BOARD ********************
$.get("http://localhost:9000/getPlayersScoreBoard", {}, function (res) {
let data = res;
console.log(res);
for (i = 0; i < data.length; i++) {
let name = data[i].name;
let score = data[i].score;
console.log(data[i].name);
$("#leadership").append("<tr><td class=\"name\">"
+ data[i].name + "</td><td class=\"score\">"
+ data[i].score + "</td></tr>");
}
});
});
//Bar Chart Leadership Code
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: [name],
datasets: [{
label: 'Leadership Board',
data: [score],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
One of the reasons why you're seeing score is not defined is because you're trying to access it outside of the scope - score will only be available inside of the for loop you've created to generate a table:
for (i = 0; i < data.length; i++) {
var name = data[i].name
var score = data[i].score
// `name` and `score` are available here
}
// `name` and `score` are NOT available here
Also, the chart expects an array for the labels and data properties, but in your example, since you're trying to use name and score variables, they'd only refer to a single item in that array:
var chart = new Chart(ctx, {
data: {
labels: ["David", "Hugo", ...],
datasets: [{
data: [55, 30, ...]
}]
}
})
Here's a solution that fixes your logic. Note that I omitted the stuff that was irrelevant and created a fake API call to try to replicate your example as close as possible:
$(function() {
get("http://localhost:9000/getPlayersScoreBoard", {}, function(res) {
function renderTable(items) {
items.forEach(item => {
var name = item.name;
var score = item.score;
$("#leadership").append(
'<tr><td class="name">' +
name +
'</td><td class="score">' +
score +
"</td></tr>"
);
});
}
function renderChart(items) {
var ctx = document.getElementById("myChart");
var names = items.map(item => item.name);
var scores = items.map(item => item.score);
var chart = new Chart(ctx, {
type: "bar",
data: {
labels: names,
datasets: [
{
label: "Leadership Board",
data: scores
}
]
},
options: {
scales: {
yAxes: [
{
ticks: {
beginAtZero: true
}
}
]
}
}
});
}
renderTable(res);
renderChart(res);
});
});
// Fake `get` request
function get(url, options, callback) {
function getRandomScore(min, max) {
return Math.floor(Math.random() * max) + min;
}
var data = [
{ _id: 1, name: "David", score: getRandomScore(0, 100) },
{ _id: 1, name: "Hugo", score: getRandomScore(0, 100) },
{ _id: 1, name: "Kayleigh", score: getRandomScore(0, 100) },
{ _id: 1, name: "Craig", score: getRandomScore(0, 100) },
{ _id: 1, name: "Kayz", score: getRandomScore(0, 100) }
];
callback(data);
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<div id="leadership"></div>
<canvas id="myChart"></canvas>
</div>
We create a function for drawing chart
<script>
$(function() {
//TESTING FOR LEADERSHIP BOARD ********************
$.get("http://localhost:9000/getPlayersScoreBoard", {}, function (res) {
let data = res;
console.log(res);
let totalLength = data.length;
for (i = 0; i < data.length; i++) {
let name = data[i].name;
let score = data[i].score;
console.log(data[i].name);
if(i == totalLength-1)
{
drawChart(data);
}
$("#leadership").append("<tr><td class=\"name\">"
+ data[i].name + "</td><td class=\"score\">"
+ data[i].score + "</td></tr>");
}
});
});
function drawChart(data)
{
var nameArr = data.map(dataValue => dataValue.name);
var scoreArr = data.map(dataValue => dataValue.score);
//Bar Chart Leadership Code
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'bar',
data: {
labels: nameArr,
datasets: [{
label: 'Leadership Board',
data: scoreArr,
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255,99,132,1)'
],
borderWidth: 1
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
}

Why is this "not defined"

I am using Express and an API to get some data which I then pass into my view. I can loop through that data and print it from within my EJS template, so I know It's there is some capacity. However when I try to use that data in a chart.js chart (all in the same template file) it says it is "not defined"... Why is this happening?
App.js:
app.get('/search', function(req, res) {
var query = req.query.search;
endPoint = 'https://api.iextrading.com/1.0/stock/' + query + '/chart/1d';
request(endPoint, function(error, response, body) {
if(!error && response.statusCode == 200) {
stockData = JSON.parse(body);
console.log(stockData);
res.render('search.ejs', {stockData : stockData});
} else {
console.log(error);
}
});
});
EJS Template file
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [minute['minute']],
datasets: [{
label: '# of Votes',
data: minute['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
EDIT
If I change it to be like this it then says that "stockData" is undefined:
<% stockData.forEach(function(minute) { %>
<canvas id="myChart" width="400" height="400"></canvas>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: [stockData['open']],
datasets: [{
label: '# of Votes',
data: stockData['open'],
backgroundColor: [
'rgba(255, 99, 132, 0.2)'
]
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero:true
}
}]
}
}
});
</script>
<% }) %>
Your stockData in chart.js is generate by javascript on browser. But stockData that really hold value that not undefine is generate by Nodejs on backend. If you wanna use like this. First, you need to render your ejs page, then send an ajax to server, get the response data. Then use that data you just receive to draw your chart. Somethings like this:
axios.get('/search')
.then(function (response) {
let data = response.data;
new Chart(document.getElementById('line-chart'), {
type: 'line',
data: {
labels: [],
datasets: [{
data: [your_response_data_from_nodejs.open],
label: 'Blabla',
borderColor: '#52D0C4',
fill: false
}
]
},
options: {
title: {
display: true,
text: 'Blala '
}
}
});
})
.catch(function (error) {
throw new error;
});

Realm-js: Cannot access realm that has been closed

Realm keeps throwing this error in a simple use case:
Cannot access realm that has been closed
My files:
RealmExample.js
import Realm from 'realm';
class Item {}
Item.schema = {
name: 'Item',
properties: {
name: 'string',
date: 'date',
id: 'string'
},
};
export default new Realm({schema: [Item]});
app.js
//My imports
export default class App extends Component<{}> {
render() {
return (
<RealmProvider realm={realm}>
<ConnectedExample />
</RealmProvider>
);
}
}
ConnectedExample.js
import React, { Component } from 'react';
import {
Text,
ScrollView,
TouchableOpacity,
View,
StyleSheet,
} from 'react-native';
import uuid from 'uuid';
import { connectRealm } from 'react-native-realm';
import ConnectedExampleItem from './ConnectedExampleItem';
const styles = StyleSheet.create({
screen: {
paddingTop: 20,
paddingHorizontal: 10,
backgroundColor: '#2a2a2a',
flex: 1,
},
add: {
height: 44,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 10,
backgroundColor: '#1a1a1a',
},
addText: {
color: 'white',
},
});
class ConnectedExample extends Component {
count = 0;
onPressAddItem = () => {
const { realm } = this.props;
realm.write(() => {
realm.create('Item', {
name: this.count.toString(),
date: new Date(),
id: uuid.v4(),
});
this.count++;
});
};
render() {
return (
<View style={styles.screen}>
<TouchableOpacity onPress={this.onPressAddItem} style={styles.add}>
<Text style={styles.addText}>Add Item</Text>
</TouchableOpacity>
<ScrollView>
{this.props.items.map((item) => (
<View key={item.id}>
<ConnectedExampleItem id={item.id} />
</View>
))}
</ScrollView>
</View>
);
}
}
export default connectRealm(ConnectedExample, {
schemas: ['Item'],
mapToProps(results, realm) {
return {
realm,
items: results.items.sorted('date') || [],
};
},
});
ConnectedExampleItem.js
import React, {
Component,
PropTypes,
} from 'react';
import {
StyleSheet,
TouchableOpacity,
Text,
} from 'react-native';
import { connectRealm } from 'react-native-realm';
const styles = StyleSheet.create({
item: {
height: 44,
justifyContent: 'center',
paddingHorizontal: 10,
marginTop: 10,
backgroundColor: 'cyan',
},
});
class ConnectedExampleItem extends Component {
onPressRemoveItem = (item) => {
const { realm } = this.props;
realm.write(() => {
realm.delete(item);
});
};
render() {
return (
<TouchableOpacity
onPress={() => this.onPressRemoveItem(this.props.item)}
style={styles.item}
>
<Text>{this.props.item.name}</Text>
</TouchableOpacity>
);
}
}
export default connectRealm(ConnectedExampleItem, {
schemas: ['Item'],
mapToProps(results, realm, ownProps) {
return {
realm,
item: results.items.find(item => item.id === ownProps.id),
};
},
});
The strange thing is that when running this code on my project I run into the Cannot access realm that has been closed (I haven't instantiated Realm anywhere else), however, if I run the example in the react-native-realm repo, it runs fine.
Also, the introduction example on the Realm documentation page runs fine as well.
What could be the issue?
Thank you.
PS: Running on React-native 0.51, Android device 6.0.

Resources