chartjs-plugin-datasource implementation in React (react2chartjs) - javascript

as the title suggest, i am trying to import data from Excel and display it as charts using ChartJs. Got some idea based on this question : (Import data from Excel and use in Chart.js) and got it working.
But what im trying to do now is to implement this in React using react2chartjs. But failed to do so, got error 'ChartDataSource' is not defined. When i did define it like this:-
import ChartDataSource from 'chartjs-plugin-datasource';
The error resides but the chart display no information
Any idea?.
THE CODE
import React, {Component} from "react";
import {Bar} from 'react-chartjs-2';
import 'chartjs-plugin-datasource';
class Chart extends Component {
constructor(props){
super(props);
this.state = {
chartData:{
datasets: [
{
hoverBorderWidth:5,
hoverBorderColor:'black',
pointStyle: 'rect',
backgroundColor:'green'
//backgroundColor:Array.apply(null, Array(4)).map(_ => getRandomColor())
},
{
hoverBorderWidth:5,
hoverBorderColor:'black',
pointStyle: 'rect',
backgroundColor:'red'
//backgroundColor:Array.apply(null, Array(4)).map(_ => getRandomColor())
}
]
}
}
}
render() {
const option = {
title:{
display:true,
text: 'Test Chart',
fontSize:23,
},
legend:{
display:true,
position:'top',
labels:{
fontColor:'Black',
usePointStyle: true
}
},
layout:{
padding:{
left:0,
right:0,
bottom:0,
top:0
}
},
tooltips:{
enabled:true
},
scales: {
yAxes: [{
ticks: {
suggestedMin: 0,
}
}]
},
plugins: {
datasource: {
url: 'Book1.xlsx',
rowMapping: 'index'
}
}
}
return (
<div className="Chart">
<Bar
data={this.chartData}
options={option}
plugins={ChartDataSource}
/>
</div>
)
}
}
export default Chart;

I know this is old, but in case anyone else comes here for a similar issue.
<Bar
data={chartData}
plugins={[ChartDataSource]}// pass as array
options={option}
/>
and url: 'Book1.xlsx' means you have Book1.xlsx in Your project\public\Book1.xlsx

Related

Primevue Chart not rendered

Im stuck with rendering Chart from Primevue components. It's based on chart.js library. At this moment I have simple vue component created:
<template>
<div class="p-chart">
<h2>Chart:</h2>
<chart type="line" :data="chartData" />
</div>
</template>
<script>
import Chart from "primevue/chart";
export default {
data() {
return {
chartData: {
labels: ["Label"],
datasets: [
{
label: "Dataset",
backgroundColor: "#5F5F5F",
data: [99],
},
],
},
};
},
components: {
Chart,
},
};
</script>
Unfortunately the chart does not appear moreover I don't see any js erros in brwoser console. Can someone help what I'm missing here? Any additional setup needed?
Remove chart.js and then install this version, worked for me (but i use vue 3, maybe you need another version) :
npm i chart.js#2.9.4
According to documentation on https://www.primefaces.org/primevue/showcase/#/chart/line
I think you are missing the options attribute inside Chart tag:
<chart type="line" :data="chartData" :options="chartOptions" />
And put the object inside the data return from vue export:
data() {
return {
chartData: {
labels: ["Label"],
datasets: [
{
label: "Dataset",
backgroundColor: "#5F5F5F",
data: [99],
},
],
},
chartOptions: {
plugins: {
legend: {
labels: {
color: '#495057'
}
}
},
scales: {
x: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
},
y: {
ticks: {
color: '#495057'
},
grid: {
color: '#ebedef'
}
}
}
}
};

Chart JS will not re-render in React

I'm having trouble getting my Chart from Chart.js to update. I'm using React so it should re-render every time I modify my state, however it is not behaving the way I would like.
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) {
chartDataSeason = [];
xLabelsSeason = [];
let seasonNum = e.target.value.slice(6)
console.log(seasonNum);
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,
});
// 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: 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>
);
}
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>
);
}
}
}
So right now, chartIt displays a graph and that works fine. However in the handleChange(e) method, I am trying to reset the data the Chart.js calls from by first setting chartDataSeason = []; and then xLabelsSeason = [];.
I am able to console.log the data and it is all there, I am unable to get it to render in a new Line chart though.
Any help would be appreciated. Also any tips on better organizing my code would be helpful, I am pretty new to React and I realize there is probably a better way to do all of this.
When setState is called inside handleChange it will tell React to re-render the Graph component. There are some special cases where this would not happen (PureComponent or others) but as this is a plain component, it should call the render function. You can add a console.log there to confirm it.
However, even if render method is called, that doesn't mean that all the children components will be re-render too. React has a smart way to detect if a component in the tree needs to update or not, and it will only do it when necessary for performance reasons.
For this case in particular, there are a few options you can try:
The this.state.chartItSeason is the actual data passed to the Line component, and the one which should be updated. Because the component is updating nested properties, React is probably not detecting it like a prop change. Try to replace the object reference, for example, inside of handleChange:
this.setState({
selectValue: e.target.value,
seasonSelected: true,
chartItSeason: {
...this.state.chartItSeason
}
});
This will create a new object which Chart.js may consider as a new data when comparing if the props have changed.
You can always pass a key prop to any component that whenever it changes it will force the complete re-render of that component. That is almost always not the best approach and it should be avoided. You can read more about the use case for key in the official docs
Also, as a side note, the two top-level variables chartDataSeason and xLabelsSeason should probably be better moved to the state or as instance properties (e.g. this.chartDataSeason) to allow more than one component to be rendered at the same time

Highcharts maxWidth or maxHeight of map

I'm trying to set the maxWidth of my world map with highcharts but it doesn't change the size.
I've tried these:
responsive: {
rules: [{
condition: {
maxWidth: 500
},
...
As suggested here and demo.
chart: {
maxWidth: 1024
},
As suggested here, demo.
I'm using the code from this example.
Update 10 Feb 2020:
Following the suggestion below, I use this code which can be found here:
import * as React from 'react';
import * as ReactDom from 'react-dom';
import * as Highcharts from 'highcharts/highmaps';
import HighchartsReact from 'highcharts-react-official';
import mapDataWorld from '#highcharts/map-collection/custom/world.geo.json';
var data: [string, number][] = [
// data can be found in the link
];
const options: Highcharts.Options = {
chart: {
height: 400
},
series: [{
type: 'map',
mapData: mapDataWorld,
data: data,
}],
responsive: {
rules: [{
chartOptions: {
chart: {
height: 800
},
legend: {
margin: 0
},
title: {
margin: 0
},
},
condition: {
maxWidth: 1024
}
}]
}
}
const App = (props: HighchartsReact.Props) => <div style={{
maxWidth: '1024px',
}}>
<HighchartsReact
options={options}
highcharts = { Highcharts }
constructorType={'mapChart'}
{...props}
/>
</div>
ReactDom.render(<App />, document.getElementById('root'));
Then open the result in a new window.
You'll see the map is pushed below the top of the screen in mobile view in Google Chrome. This still happens with margin: 0.
responsive: {
rules: [{
condition: {
maxWidth: 500
},
...
Above option is a condition for which the next defined chart option will be applied, in case of shared demo is:
chartOptions: {
xAxis: {
labels: {
formatter: function () {
return this.value.charAt(0);
}
}
},
yAxis: {
labels: {
align: 'left',
x: 0,
y: -2
},
title: {
text: ''
}
}
}
Highcharts doesn't offer a chart.maxWidth feature, only the chart.width feature as you shared from the API to set the fixed value for chart width.
You can set the max-width for the div where Highcharts map is rendering, but remember about importing CSS.
Demo: https://stackblitz.com/edit/react-ts-5upg3v?file=index.tsx
Or without CSS as inline-style - demo: https://stackblitz.com/edit/react-ts-uy5xkd?file=index.tsx

Highcharts 3D render problem on setState()

I am implementing react highchart 3d pie chart. Whenever I try to use this.setState() in lifecycle method componentDidMount(), the 3d chart displaces from it's position to right side diagonally. And if I remove this.setState() from componentDidMount(), it gets back to original position fine.
I am unable to fetch api call because of this odd behaviour as it is existing only in 3d pie charts. If I use 2d pie charts, setState() inside componentDidMount(), it works fine then.
This is how my problem goes:
App.js
import React, { Component } from 'react'
import HighchartsReact from 'highcharts-react-official'
import Highcharts from 'highcharts';
import highcharts3d from 'highcharts/highcharts-3d';
highcharts3d(Highcharts);
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
test: 'testing state'
}
}
componentDidMount(){
this.setState({
test: 'changing state'
})
}
render() {
const options = {
chart: {
type: 'pie',
options3d: {
enabled: true,
alpha: 45,
beta: 0
}
},
title: {
text: 'Browser market shares at a specific website, 2014'
},
tooltip: {
pointFormat: '{series.name}: <b>{point.percentage:.1f}%</b>'
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
depth: 35,
dataLabels: {
enabled: true,
format: '{point.name}'
}
}
},
series: [{
type: 'pie',
name: 'Browser share',
data: [
['Firefox', 45.0],
['IE', 26.8],
{
name: 'Chrome',
y: 12.8,
sliced: true,
selected: true
},
['Safari', 8.5],
['Opera', 6.2],
['Others', 0.7]
]
}]
}
return (
<div>
<HighchartsReact
options = {options}
highcharts={Highcharts}
/>
</div>
)
}
}
before setState({})
after setState({})
Kindly help to resolve this odd bug .
The options are always 'new' for the wrapper if you keep them in the render method. That causes calling chart.update() with the same options. I have reproduced that situation here: http://jsfiddle.net/BlackLabel/mwv1kotb/
The best way is to keep options in a state: https://github.com/highcharts/highcharts-react#optimal-way-to-update
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
test: "testing state",
chartOptions: {...}
}
}
componentDidMount() {
this.setState({
test: "changing state"
});
}
render() {
return (
<div>
<HighchartsReact
options={this.state.chartOptions}
highcharts={Highcharts}
/>
</div>
);
}
}
Live demo: https://codesandbox.io/s/highcharts-react-demo-om7n3
However, that problem looks like a bug, so I have reproduced it without React and reported here: https://github.com/highcharts/highcharts/issues/11928

Issues with react-chartjs-2

I have issue with react-chartjs-2 and chartjs-plugin-streaming, my goal was to create a live graph with stream, but it ends up in error and I don't quite know why. Anyhow, my imports are like this:
import { Chart, Bubble } from 'react-chartjs-2';
import ChartStream from 'chartjs-plugin-streaming';
Then right below that is this part:
Chart.pluginService.register(ChartStream);
and then theres's this part in component render
<Bubble
data={{
labels: ['demo'],
datasets: [{
backgroundColor: 'rgba(75,192,192,1)',
data: []
}]
}}
options={{
plugins: {
streaming: {
onRefresh: function(chart) {
chart.data.datasets[0].data.push({
x: Date.now(),
y: Math.random() * 100,
r: 5
});
},
delay: 500,
refresh: 1000,
frameRate: 30,
duration: 3600000 // 3600000 = 1hour
}
},
scales: {
xAxes: [{
type: 'realtime',
id: 'x-axis-0'
}]
}
}}
/>
first error that happens right on navigation is this:
Uncaught TypeError: Cannot set property 'options' of undefined
at core.controller.js:51
at Array.forEach ()
at n (core.controller.js:50)
at e.update (core.controller.js:340)
at e.construct (core.controller.js:121)
at new e (core.js:7)
at t.renderChart (index.js:228)
at t.componentDidMount (index.js:53)
at e.notifyAll (CallbackQueue.js:76)
at r.close (ReactReconcileTransaction.js:80)
because in core.controller.js of chartjs is this part:
function updateConfig(chart) {
var newOptions = chart.options;
// Update Scale(s) with options
if (newOptions.scale) {
chart.scale.options = newOptions.scale;
} else if (newOptions.scales) {
newOptions.scales.xAxes.concat(newOptions.scales.yAxes).forEach(function(scaleOptions) {
chart.scales[scaleOptions.id].options = scaleOptions;
});
}
// Tooltip
chart.tooltip._options = newOptions.tooltips;
}
the part that fails is this:
chart.scales[scaleOptions.id].options = scaleOptions;
and it's caused by these options I set before, upon debugging there is no x-axis-0 in chart.scales, only y-axis-0
scales: {
xAxes: [{
type: 'realtime',
id: 'x-axis-0'
}]
}
Anyone know how to work around this issue?
The problem seems that when a chart instance is constructed, the 'realtime' scale is not registered yet, and chart.scales['x-axis-0'] is left undefined. Please make sure the chartjs-plugin-streaming is imported before a chart is constructed.
By the way, you don't need to register the plugin object to the pluginService explicitly. It is done with import 'chartjs-plugin-streaming'. Try this working sample:
import React from 'react';
import ReactDOM from 'react-dom';
import { Bubble } from 'react-chartjs-2';
import 'chartjs-plugin-streaming';
ReactDOM.render(
<Bubble
data={{
datasets: [{
label: 'demo',
backgroundColor: 'rgba(75,192,192,1)',
data: []
}]
}}
options={{
plugins: {
streaming: {
onRefresh: function(chart) {
chart.data.datasets[0].data.push({
x: Date.now(),
y: Math.random() * 100,
r: 5
});
},
delay: 500,
refresh: 1000,
frameRate: 30,
duration: 3600000 // 3600000 = 1hour
}
},
scales: {
xAxes: [{
type: 'realtime'
}]
}
}}
/>
, document.getElementById('root'));

Categories

Resources