Looping through an array to render multiple charts - javascript

I am creating a webapp in react that shows data based on Economic, social, and political indicators. Since the data is too big to render on to one single chart (a nightmare for mobile UI) I've decided to split the data into 4 objects in an array. I am trying to find a way to loop through that array. Here is the code
import React from 'react';
import { Bar } from 'react-chartjs-2';
import ChartDataSource from 'chartjs-plugin-datasource';
const config = {
data: {
datasets: [
{
backgroundColor: 'rgba(255,99,132,0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
hoverBackgroundColor: 'rgba(255,99,132,0.4)',
hoverBorderColor: 'rgba(255,99,132,1)',
},
],
},
options: {
maintainAspectRatio: true,
plugins: {
datasource: {
type: 'sheet',
url: 'GDP1.xlsx', {/*<----- This is where the array gets looped*/}
rowMapping: 'index',
},
},
title: {
display: true,
text: 'GDP per country',
},
scales: {
xAxes: [
{
scaleLabel: {
display: true,
labelString: 'Countries',
},
},
],
yAxes: [
{
id: '2018',
gridLines: {
drawOnChartArea: false,
},
scaleLabel: {
display: true,
labelString: 'GDP',
},
},
],
},
},
};
function BarChart() {
const graphslist = [ {/* Ths is the array i want to loop through*/}
{ url: 'GDP1.xlsx' },
{ url: 'GDP2.xlsx' },
{ url: 'GDP3.xlsx' },
{ url: 'GDP4.xlsx' },
];
return (
<div>
<h2>GDP per country</h2>
<Bar
data={config.data}
options={config.options}
width={100}
height={100}
plugins={[ChartDataSource]}
/>
</div>
);
}
export default BarChart;
I've been trying to use the .map function so config.options.plugins.url loops over the 4 objects in the array therefore rendering 4 charts but I keep getting unexpected token. Any help is welcome

you can try this without the plugins.
const theDatasets= (()=>{
let arr = []
for (let i = 0; i < graphslist .length; i++) {
arr.push(
{
data: graphslist [i],
backgroundColor: 'rgba(255,99,132,0.2)',
borderColor: 'rgba(255,99,132,1)',
borderWidth: 1,
hoverBackgroundColor: 'rgba(255,99,132,0.4)',
hoverBorderColor: 'rgba(255,99,132,1)',
label: graphslist [i],
}
)
}
return arr
})()
on the render
<Bar
data={theDatasets}
options={config.options} // your option without plugins
width={100}
height={100}
/>

Related

Object is not rendering

I am building a covid-19 tracker using react charts but when my data is displayed on the screen it does not come out be as expected.
This is my chart code
<div >
{data?.length > 0 && (
<Line
data={{
datasets: [
{
backgroundColor: "rgba(204, 16, 52, 0.5)",
borderColor: "#CC1034",
data: data,
},
],
}}
options={options}
/>
)}
</div>
[enter image description here][1]
These are my option parameters
const options = {
legend: {
display: false,
},
elements: {
point: {
radius: 0,
},
},
maintainAspectRatio: false,
tooltips: {
mode: "index",
intersect: false,
callbacks: {
label: function (tooltipItem, data) {
return numeral(tooltipItem.value).format("+0,0");
},
},
},
scales: {
xAxes: [
{
type: "time",
time: {
format: "MM/DD/YY",
tooltipFormat: "ll",
},
},
],
yAxes: [
{
gridLines: {
display: false,
},
ticks: {
// Include a dollar sign in the ticks
callback: function (value, index, values) {
return numeral(value).format("0a");
},
},
},
],
},
};
My graph is coming out to be stretched and its height is increasing.

Chartjs in Vue integrate parsed Json Data

i have amateur problems with building a Chart with Chart.js in Vue.js. Chart.js works, I can already see a Chart and am able log the data (with console.log) in the command line.
chart-data.js
type: 'line',
data: {
labels: ['Afghanistan', 'Somalia', 'Nigeria'],
datasets: [
{ // one line graph
label: 'Number of Moons',
data: [300, 75, 468],
backgroundColor: [
'rgba(54,73,93,.5)', // Blue
],
borderColor: [
'#36495d',
],
borderWidth: 3
},
{ // another line graph
label: 'Planet Mass (x1,000 km)',
data: [4.8, 12.1, 12.7, 6.7, 139.8, 116.4, 50.7, 49.2],
backgroundColor: [
'rgba(71, 183,132,.5)', // Green
],
borderColor: [
'#47b784',
],
borderWidth: 3
}
]
},
options: {
responsive: true,
lineTension: 1,
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
padding: 25,
}
}]
}
}
}
export default planetChartData;
The parsing already works, but im struggling with getting my arrays (labelNation, numOfNationals) into the variable planetChartData.
json-parse.js
const file = fs.readFileSync('./data.json', 'utf8')
const test = JSON.parse(file)
let nationality = [];
for (let i = 0; i < test.length; i++) {
nationality.push(test[i].nationality)
}
const labelNation = Array.from(new Set(nationality));
let numOfNationals = [];
for (let i = 0; i < labelNation.length; i++) {
numOfNationals.push(nationality.filter(nat => nat === labelNation[i]).length)
}
vue.js
(in the script tag)
const Chart = require('chart.js');
import planetChartData from './chart-data.js';
export default {
name: 'App',
components: {
},
data() {
return {
planetChartData: planetChartData,
}
},
mounted() {
this.createChart('planet-chart', this.planetChartData);
},
methods: {
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
const myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
}
}
}
Thanks a lot for your help!
can you try this:
const Chart = require('chart.js');
import planetChartData from './chart-data.js';
export default {
name: 'App',
components: {
},
data() {
return {
planetChartData: planetChartData,
}
},
mounted() { // vvv use spread operator here below
this.createChart('planet-chart', ...this.planetChartData);
},
methods: {
createChart(chartId, chartData) {
const ctx = document.getElementById(chartId);
const myChart = new Chart(ctx, {
type: chartData.type,
data: chartData.data,
options: chartData.options,
});
}
}
}
and your chart-data.js should look like this for a decent export:
i am initializing a const for declaration of planetChartData
const planetChartData = {
type: "line",
data: {
labels: ["Afghanistan", "Somalia", "Nigeria"],
datasets: [
{
// one line graph
label: "Number of Moons",
data: [300, 75, 468],
backgroundColor: [
"rgba(54,73,93,.5)" // Blue
],
borderColor: ["#36495d"],
borderWidth: 3
},
{
// another line graph
label: "Planet Mass (x1,000 km)",
data: [4.8, 12.1, 12.7, 6.7, 139.8, 116.4, 50.7, 49.2],
backgroundColor: [
"rgba(71, 183,132,.5)" // Green
],
borderColor: ["#47b784"],
borderWidth: 3
}
]
},
options: {
responsive: true,
lineTension: 1,
scales: {
yAxes: [
{
ticks: {
beginAtZero: true,
padding: 25
}
}
]
}
}
};
export default planetChartData;

Adding condition in ComponentDidMount to display chart data

I've added a condition in componentDidMount to showcase data for either 1 or 2 datasets based on the pros recieved. However, I'm getting an error in the if condition. Here is BarChart component. Inside it, I've already imported Chart from 'chart.js'.
class BarChart1 extends Component {
constructor(props) {
super(props);
}
chartRef = React.createRef();
componentDidMount() {
const myChartRef = this.chartRef.current.getContext("2d");
let dataCheck = null;
if(this.props.data2){
let dataCheck = {
labels: this.props.labels,
datasets: [
{
label: this.props.label1,
backgroundColor: 'rgba(26,179,148,0.5)',
borderColor: "rgba(26,179,148,0.7)",
data: this.props.data1
}, {
label: this.props.label2,
backgroundColor: 'rgba(220, 220, 220, 0.5)',
pointBorderColor: "#fff",
data: this.props.data2
}
]
}
}
else {
let dataCheck = {
labels: this.props.labels,
datasets: [
{
label: this.props.label1,
backgroundColor: 'rgba(26,179,148,0.5)',
borderColor: "rgba(26,179,148,0.7)",
data: this.props.data1
}
]
}
}
new Chart(myChartRef, {
type: 'bar',
data: {dataCheck},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: true,
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true
},
gridLines: {
display: false
}
}],
xAxes: [{
gridlines: {
display: false
},
ticks: {
fontSize: 9
}
}]
}
},
plugins: [{
beforeInit: function(chart) {
chart.data.labels.forEach(function(e, i, a) {
if (/\n/.test(e)) {
a[i] = e.split(/\n/);
}
});
}
}]
})
}
render() {
return <canvas ref={this.chartRef} />
}
}
I've edited my question. I'm now getting a blank chart without the error.
There are two errors in your code.
Your have to end the variable declaration with ; instead of ,
You have to declare datacheck outside the if else block unless you want it undefined
componentDidMount() {
...
let dataCheck;
if(this.props.data2){
dataCheck = {
...
}
}
else {
dataCheck = {
...
}
}
}

Variable is losing all of its data upon a React re-render

I have a React project that utilizes Chart JS to plot charts based on data.
I have a drop down list selector to select which dataset to draw from. However, after I switch to another dataset and then switch back to the initial rendered dataset, it does not appear to work anymore. Here is my code:
import React from 'react';
import { Line } from 'react-chartjs-2';
import { chartData } from './Search.js';
import { xLabels } from './Search.js';
import { allEpsData } from './Search.js';
let chartDataSeason = [];
let xLabelsSeason = [];
export class Graph extends React.Component {
constructor(props) {
super(props);
this.state = {
selectValue: '',
seasonSelected: false,
chartIt: {
labels: xLabels,
datasets: [
{
label: 'Rating',
data: chartData,
fill: false,
borderColor: '#00B4CC',
},
],
},
chartItSeason: {
labels: xLabelsSeason,
datasets: [
{
label: 'Rating',
data: chartDataSeason,
fill: false,
borderColor: '#00B4CC',
},
],
},
};
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
if (e.target.value === 'All Seasons') {
this.setState({
seasonSelected: false,
chartIt: {
labels: xLabels,
datasets: [
{
label: 'Rating',
data: chartData,
fill: false,
borderColor: '#00B4CC',
},
],
},
});
console.log(chartData);
} else {
chartDataSeason = [];
xLabelsSeason = [];
let seasonNum = e.target.value.slice(6);
for (let i = 0; i < allEpsData[seasonNum - 1].length; i++) {
chartDataSeason.push(allEpsData[seasonNum - 1][i].imdbRating);
xLabelsSeason.push(
`s${seasonNum}e${allEpsData[seasonNum - 1][i].Episode} "${
allEpsData[seasonNum - 1][i].Title
}"`
);
}
this.setState({
selectValue: e.target.value,
seasonSelected: true,
chartItSeason: {
labels: xLabelsSeason,
datasets: [
{
label: 'Rating',
data: chartDataSeason,
fill: false,
borderColor: '#00B4CC',
},
],
},
});
}
// console.log(chartDataSeason)
}
render() {
let seasons = [];
for (let i = 0; i < allEpsData.length; i++) {
seasons.push(i);
}
if (this.state.seasonSelected === false) {
return (
<div>
<select // selector
className="select"
value={this.state.selectValue}
onChange={this.handleChange}
>
<option>All Seasons</option>
{seasons.map((el) => {
return <option key={el}>season {el + 1}</option>;
})}
</select>
<div className="line-container">
<Line
data={this.state.chartIt}
width={600}
height={400}
options={{
maintainAspectRatio: false,
scales: {
xAxes: [
{
ticks: {
display: true, //this will remove only the label
},
gridLines: {
show: false,
display: false,
},
},
],
yAxes: [
{
gridLines: {
show: false,
drawBorder: false,
},
},
],
},
animation: {
duration: 200,
easing: 'easeInOutQuart',
},
tooltips: {
enabled: true,
},
}}
/>
</div>
<div className="seasons-graph-container"></div>
</div>
);
} else {
return (
<div>
<select
className="select"
value={this.state.selectValue}
onChange={this.handleChange}
>
<option>All Seasons</option>
{seasons.map((el) => {
return <option key={el}>season {el + 1}</option>;
})}
</select>
<div className="line-container">
<Line
data={this.state.chartItSeason}
width={600}
height={400}
options={{
maintainAspectRatio: false,
scales: {
xAxes: [
{
ticks: {
display: false, //this will remove only the label
},
gridLines: {
show: false,
display: false,
},
},
],
yAxes: [
{
gridLines: {
show: false,
drawBorder: false,
},
},
],
},
animation: {
duration: 200,
easing: 'easeInOutQuart',
},
tooltips: {
enabled: true,
},
}}
/>
</div>
<div className="seasons-graph-container"></div>
</div>
);
}
}
}
the problem lies with chartData, it is not getting re-rendered correctly -- all the data associated with it has been replaced with the data of the last menu item. However oddly enough, xLabels does get rendered properly. Nothing is changing the value of chartData or xLabels but only xLabels gets correctly updated. Is there anything that I'm missing here? Apologies in advance for the spaghetti code, I am still learning React!

ChartJS makes unwanted horizontal line on Safari

I'm currently working with ChartJS.
It works fine on Firefox and Chrome.
On Safari there is horizontal lines which appear. (here there is one good on orange and two others unwanted )
I have a lot of datapoint (2 lines of 1600 data point). I don't know if it could be a cause.
My code is here.
In some other part, I update time uni and time min.
_displayChart: function(label1, label2){
var timeFormat = 'DD/MM/YYYY HH:mm';
var ctx = document.getElementById(this.heading).getContext('2d');
const data = {
// Labels should be Date objects
//labels:this._stats[1],
datasets: [{
fill: false,
label: label1,
data: this._stats[2],
borderColor: '#fe8b36',
pointRadius: 0,
backgroundColor: [
'#fe8b36'
]
},{
label: label2,
fill: false,
data: this._stats[3],
borderWidth: 1,
pointRadius: 0,
borderColor: 'rgba(99, 100, 200, 1)',
backgroundColor: [
'rgba(99, 100, 200, 0.2)'
]
}]
}
const options = {
type: 'line',
data: data,
options: {
responsive: false,
fill: false,
responsive: true,
maintainAspectRatio:!this._isMobile(),
max: this.max,
hover: {
// Overrides the global setting
mode: 'index'
},
annotation: {
drawTime: 'beforeDatasetsDraw',
events: ['click'],
annotations: []
},
legend: {
labels: {
filter: function(legendItem, chartData) {
if(legendItem.datasetIndex == 2 || legendItem.datasetIndex == 3 ){
return false;
}
return true;
}
}
},
tooltips: {
mode:'index',
intersect : false,
callbacks: {
title: function(tooltipItems, data) {
return tooltipItems[0].xLabel.format(timeFormat);
},
label: function(tooltipItems, data) {
return tooltipItems.yLabel+'%';
}
}
},
scales: {
xAxes: [{
type: 'time',
display: true,
time:{
unit:'day',
min:moment(Date.now()-this.monthTs),
max:this._stats[1][this._stats[1].length]
}
}],
yAxes: [{
ticks: {
beginAtZero: true,
},
display: true
}]
}
}
}
this._chart = new Chart(ctx, options);
},
When I'm applying this, I have no problem:
this._chart.options.scales.xAxes[0].time.unit = 'month';
this._chart.options.scales.xAxes[0].time.stepSize = 1;
this._chart.options.scales.xAxes[0].time.min = this._stats[1][0];
this._chart.options.scales.xAxes[0].time.max = this._stats[1][this._stats[1].length-1];
Thanks

Categories