I am a newbie to React, Highcharts and UI developing in general.
I would like to render multiple charts from an array of data. Currently, the page displays only the last chart - from the last data in the array.
function chartToRender(seriesArr){
//console.log(JSON.stringify(application));
var Chart = React.createClass({
// When the DOM is ready, create the chart.
componentDidMount: function() {
// Extend Highcharts with modules
if (this.props.modules) {
this.props.modules.forEach(function(module) {
module(Highcharts);
});
}
// Set container which the chart should render to.
this.chart = new Highcharts[this.props.type || "Chart"](
this.props.container,
this.props.options
);
},
//Destroy chart before unmount.
componentWillUnmount: function() {
this.chart.destroy();
},
//Create the div which the chart will be rendered to.
render: function() {
return React.createElement('div', {
id: this.props.container
});
}
}), element2;
return seriesArr.map(application =>
element2 = React.createElement(Chart, {
container: 'stockChart',
type: 'stockChart',
options: {
rangeSelector: {
selected: 0
},
title: {
text: application.app_name + ' application Free Memory'
},
tooltip: {
style: {
width: '200px'
},
valueDecimals: 4,
shared: true
},
xAxis:{
type:'datetime',
//categories : timeSeries,
dateTimeLabelFormats : {
millisecond: '%Y-%m-%dT%H:%M:%S.%L'
}
},
yAxis: {
title: {
text: 'Free Memory'
}
},
series: application.series
}
})
)
}
Currently, I am calling this function through the render function on the class
render() {
var seriesArr = getSeriesArr(this.props)
return(
<div>
<div className="row">
<ul>
{chartToRender(seriesArr)}
</ul>
</div>
</div>
)
}
How can I display all the charts on the page one below the other? the "seriesArr" variable has all the data required to render the charts.
Try pushing your dynamically created Chart components into an array and then render the array. That should get you going in the right direction.
let charts =[];
charts = seriesArr.map(application =>
element2 = React.createElement(Chart, {
container: 'stockChart',
type: 'stockChart',
options: {
rangeSelector: {
selected: 0
},
title: {
text: application.app_name + ' application Free Memory'
},
tooltip: {
style: {
width: '200px'
},
valueDecimals: 4,
shared: true
},
xAxis:{
type:'datetime',
//categories : timeSeries,
dateTimeLabelFormats : {
millisecond: '%Y-%m-%dT%H:%M:%S.%L'
}
},
yAxis: {
title: {
text: 'Free Memory'
}
},
series: application.series
}
})
)
render() {
return(
<div>
<div className="row">
<ul>
{charts}
</ul>
</div>
</div>
)
}
Related
I want to have three LineCharts below each other. They are sharing same x-axis (time). I was able to create this:
However, it is very useless to have x-axis on the top and middle chart. I would prefer not to display the x-axis for the top and middle. When I tried it, I got this:
But as you can see, the last grid for 12AM is not visible for the top and middle chart, and the grids are not aligned (it looks weird). Each chart is rendered as a separate component using React.
Here are my TimelinePlot component which is rendering each LineChart (sorry for the mess, but I did not yet refactor it):
import React from 'react';
import { Line } from 'react-chartjs-2';
import { GetColors } from '../../utils/PlotDescriptionHelper';
class TimelinePlot extends React.Component {
MapPropsToData(props) {
const colors = GetColors(props.series.length);
return props.series.map((dataset, index) => {
return {
label: dataset.name,
data: dataset.data,
borderWidth: 1,
fill: false,
borderColor: colors[index],
//pointBackgroundColor: 'rgb(255,255,255)',
};
});
}
MapPropsToOptions(props) {
return {
elements: {
point: {
radius: 0,
},
},
legend: {
// display: props.showLegend,
position: 'top',
align: 'end',
},
scales: {
yAxes: [
{
ticks: props.yAxisTicks,
scaleLabel: {
display: true,
labelString: props.yAxisName + props.yAxisUnit,
},
},
],
xAxes: [
{
type: 'time',
// position: props.xAxisPosition,
time: {
parser: 'YYYY-MM-DD HH:mm',
tooltipFormat: 'll HH:mm',
},
// scaleLabel: {
// display: true,
// //labelString: 'Date',
// },
ticks: {
min: props.xAxisStart,
max: props.xAxisEnd,
//display: true,
display: props.xAxisDisplay,
},
},
],
},
};
}
render() {
const dataset = { datasets: this.MapPropsToData(this.props) };
return (
<div className='measurement-row'>
<Line
data={dataset}
options={this.MapPropsToOptions(this.props)}
position='absolute'
height='15%'
width='80%'
/>
</div>
);
}
}
And here is the render method of the parent using TimelinePlot component:
render() {
var plots = Object.keys(this.state.timeSeries).map((key, index) => {
return (
<TimelinePlot
key={key + index}
series={this.state.timeSeries[key]}
yAxisName={FirstLetterToUpper(key)}
yAxisUnit={MapKeyToUnit(key)}
xAxisDisplay={index === Object.keys(this.state.timeSeries).length - 1}
xAxisPosition={index === 0 ? 'top' : 'bottom'}
xAxisStart={this.state.startTime}
xAxisEnd={this.state.endTime}
showLegend={index === 0}
yAxisTicks={MapYAxisTicks(key)}
/>
);
});
return (
<div className='width-90'>
<TimelineDashboardHeader />
<div className='dashboard__column'>{plots}</div>
</div>
);
}
I am using Highcharts in the vue.js environment. I use https://www.highcharts.com 7.2.0 and Highcharts-Vue https://github.com/highcharts/highcharts-vue#readme 1.3.5.
I have multiple highstock charts in the same page. I am trying to get the values from all the charts into the tooltip of the highstock that I am currently hovering.
Based on this http://jsfiddle.net/mjsdnngq/14/ I am trying to create my code
This is what I have now
vue template
<template>
<div class="vessChart" v-on:mousemove="updateCoordinates" >
<v-card :loading=!this.getStreamPlots.done>
<highcharts ref="chart" :constructor-type="'stockChart'" :callback="chartcallback" :options="options" ></highcharts>
</v-card>
</div>
</template>
This is the "options" as I define it for the chart as a vue data object
options: {
tooltip: {
shared: true,
pointFormat: '{series.name}: {point.y}<br>'
},
chart: {
type: "line",
zoomType: 'x'
},
title: {
text: this.streamChart.title
},
rangeSelector: {
inputEnabled: false
},
xAxis: {
ordinal: false,
type: 'datetime',
tickInterval: 5 * 60 * 1000 ,
labels: {
enabled:false
},
events : {
afterSetExtremes : this.afterSetExtremes
}
},
yAxis: {
title: {
text: this.streamChart.title
},
labels: {
enabled:true
},
alignTicks:'left',
textAlign:'left',
align:'middle',
opposite:false
},
plotOptions: {
series: {
animation: false
}
},
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
chart: {
height: 300
},
subtitle: {
text: null
},
navigator: {
enabled: false
},
legend: {
enabled: true
},
yAxis: {
title: {
enabled: false
}
}
}
}]
},
series: [
{
name: this.streamChart.title,
pointStart: 1546300800000,
pointInterval: 5 * 60 * 1000
}
]
},
when the Highstock loads, I use callback, to get it and set it as a local vue var and also push it in an array in the vuex store, so I can have all charts available.
chartcallback(hc){
this.chart=hc;
this.currentId = this.streamChart.id;
this.chartData = this.chartData.concat(this.getStreamPlots[this.currentId].data);
this.addStreamCharts(hc); //push it to an array in vuex store
this.chart.series[0].setData(this.chartData, true, true, true);
},
and then I use the updateCoordinates to get all the values from all the charts I saved in vuex store, using the searchPoint. Then, feed the values to the tooltip of the chart I am currently hovering
updateCoordinates(e){
if (this.getStreamPlots.done && this.chart != null) {
let hce = this.chart.pointer.normalize(e);
let points=[];
this.getStreamCharts.forEach(chart => {
let point = chart.series[0].searchPoint(hce, true);
points.push(point);
});
this.chart.tooltip.refresh(points);
}
}
Problems :
1- This works but it shows all the values to the first chart I hovered. For example, I hover to the first chart, I get all the values into the tooltip as I should. But then I hover to the third chart and the values are still showed in the first chart tooltip. No tooltip in the third chart.
2- The values are in different tooltips. I would like for all the values be in one tooltip. (check the image)
Thank you
So what I have is a chart in which I have events function which loads data for multiple sets.
suppose I have data of 3000 points. The first data set renders the first 1000 points and after that second data set renders 2000 points.
for which I am calling my 'events' function .
but the problem arises that after showing the first 1000 set of data. The chart starts from the begining.
I don't want that.
That's why I need a solution so that my Highchart's chart render only once and the event function loads continuously.
Here's a snip of my Highchart's js
Highcharts.chart("chartcontainer", { // make thsi chart load only once.
chart: {
type: 'line',
animation: Highcharts.svg, // don't animate in old IE
marginRight: 10,
//Load this event function as the data updates
events: {
load: function() {
var series = this.series[0],
chart = this;
setInterval(function() {
//some logic regarding the chart
//..
v = {
y: y,
x: x
};
console.log("V value", v);
series.addSeries(v, false, true);
counter++;
localcounter++;
} else
{
oldcounter=counter;
flagToreload=1;
}
}, 1000/130);
setInterval(function() {
chart.redraw(false);
}, 100);
}
}
},
time: {
useUTC: false
},
title: {
text: 'Live random data'
},
xAxis: {
type: 'Value',
gridLineWidth: 1
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}],
gridLineWidth: 1
},
tooltip: {
headerFormat: '<b>{series.name}</b><br/>',
pointFormat: '{point.x:%Y-%m-%d %H:%M:%S}<br/>{point.y:.2f}'
},
exporting: {
enabled: false
},
series: [{
animation: false,
name: 'Random data',
data: (function() {
// generate an array of random data
var data = [],
time = counter,
i;
for (i = -1000; i <= 0; i += 1) {
data.push([
counter,
null
]);
}
return data;
}())
}]
});
You can use:
addPoint method:
chart: {
events: {
load: function() {
var newData,
chart = this,
series = chart.series[0];
setInterval(function() {
newData = getRandomData();
newData.forEach(function(el) {
series.addPoint(el, false);
});
chart.redraw();
}, 2000);
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/2a8qswhf/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#addPoint
setData method:
chart: {
events: {
load: function() {
var newData,
chart = this,
combinedData,
series = chart.series[0];
setInterval(function() {
newData = getRandomData();
combinedData = series.userOptions.data.concat(newData);
series.setData(combinedData);
}, 2000);
}
}
}
Live demo: http://jsfiddle.net/BlackLabel/Lmsk8yw9/
API Reference: https://api.highcharts.com/class-reference/Highcharts.Series#setData
For the past couple days, I was struggling to use this highchart map type in my react project
https://jsfiddle.net/26tbkjov//
Can some one please help me out?
Please check what I achieved until now:
https://codesandbox.io/s/highcharts-react-demo-0m5ux
I am using those highcharts npm packages
"highcharts": "^7.1.2",
"highcharts-react-official": "^2.2.2",
I have tried many things and ended up in a dead path.. the following is the last thing i have tried:
import React from "react";
import mapData from '../../api/mapData';
import Highcharts from 'highcharts';
import HighchartsReact from 'highcharts-react-official';
require('highcharts/modules/map')(Highcharts);
class MyMap extends React.Component {
constructor(props) {
super(props);
this.state = {
mapValues: [],
modalClassic: false,
};
this.mapData = new mapData();
// preparing the config of map with empty data
this.options = {
title: {
text: 'Widget click by location',
style: {
color: '#fff'
},
},
chart:{
backgroundColor: 'transparent',
type: 'map',
map: null,
},
mapNavigation: {
enabled: true,
enableButtons: false
},
credits: {
enabled: false
},
colorAxis: {
dataClasses: [
{
from: 1,
color: '#C40401',
name: 'widget name one'
}, {
from: 2,
color: '#0200D0',
name: 'widget name two'
}
]
},
tooltip: {
pointFormatter: function() {
return this.name;
}
},
legend: {
align: 'right',
verticalAlign: 'top',
x: -100,
y: 70,
floating: true,
layout: 'vertical',
valueDecimals: 0,
backgroundColor: ( // theme
Highcharts.defaultOptions &&
Highcharts.defaultOptions.legend &&
Highcharts.defaultOptions.legend.backgroundColor
) || 'rgba(255, 255, 255, 0.85)'
},
series: [{
name: 'world map',
dataLabels: {
enabled: true,
color: '#FFFFFF',
format: '{point.postal-code}',
style: {
textTransform: 'uppercase'
}
},
tooltip: {
ySuffix: ' %'
},
cursor: 'pointer',
joinBy: 'postal-code',
data: [],
point: {
events: {
click: function(r){
console.log('click - to open popup as 2nd step');
console.log(r);
}
}
}
}]
};
}
/*
* Before mounting the component,
* update the highchart map options with the needed map data and series data
* */
componentWillMount = () => {
this.mapData.getWorld().then((r)=>{
this.setState({'mapData': r.data}, ()=>{
this.options.series[0].data = []; //make sure data is empty before fill
this.options['chart']['map'] = this.state.mapData; // set the map data of the graph (using the world graph)
// filling up some dummy data with values 1 and 2
for(let i in this.state.mapData['features']){
let mapInfo = this.state.mapData['features'][i];
if (mapInfo['id']) {
var postalCode = mapInfo['id'];
var name = mapInfo['properties']['name'];
var value = i%2 + 1;
var type = (value === 1)? "widget name one" : "widget name two";
var row = i;
this.options.series[0].data.push({
value: value,
name: name,
'postal-code': postalCode,
row: row
});
}
}
// updating the map options
this.setState({mapOptions: this.options});
});
});
}
render() {
return (
<div>
{(this.state.mapData)?
<HighchartsReact
highcharts={Highcharts}
constructorType={'mapChart'}
options={(this.state.mapOptions)? this.state.mapOptions: this.options}
/>
: ''}
</div>
);
}
}
export default MyMap;
If you want to use the USA map, you need to change the url to: "https://code.highcharts.com/mapdata/countries/us/us-all.geo.json" and the postal-code from US.MA to MA:
this.mapData.getWorld().then(r => {
...
for (let i in this.state.mapData["features"]) {
...
var postalCode = mapInfo.properties["postal-code"];
...
}
...
});
Live demo: https://codesandbox.io/s/highcharts-react-demo-jmu5h
To use the word map, you need to also change the part related with the postal-code and joinBy property:
series: [{
joinBy: ['iso-a2', 'code'],
...
}]
this.mapData.getWorld().then(r => {
...
for (let i in this.state.mapData["features"]) {
let mapInfo = this.state.mapData["features"][i];
if (mapInfo["id"]) {
var code = mapInfo["id"];
...
this.options.series[0].data.push({
"code": code,
...
});
}
}
...
});
Live demo: https://codesandbox.io/s/highcharts-react-demo-sxfr2
API Reference: https://api.highcharts.com/highmaps/series.map.joinBy
I am trying to integrate highcarts inside the react component.
Here is my code for the react component
import * as Highcharts from 'highcharts/highmaps'
class RealTime extends Component {
componentDidMount() {
$.getJSON( 'https://cdn.jsdelivr.net/gh/highcharts/highcharts#v7.0.0/samples/data/world-population-density.json',
function(data) {
$.each(data, function() {
this.value = this.value < 1 ? 1 : this.value
})
Highcharts.mapChart('world_map', {
chart: {
map: 'custom/world'
},
title: {
text: 'Fixed tooltip with HTML'
},
legend: {
title: {
text: 'Population density per kmĀ²',
style: {
color:
(Highcharts.theme && Highcharts.theme.textColor) || 'black'
}
}
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
series: [
{
data: data,
mapData: Highcharts.maps['custom/world'],
joinBy: ['iso-a3', 'code3'],
name: 'Population density',
states: {
hover: {
color: '#a4edba'
}
}
}
]
})
}
)
}
render() {
return (
<div>
<div className="map_bg" id="world_map"/>
</div>
)
}
}
But the above code does not show either the map or any error in my react component . Can Anyone please help me
What i am missing here?
Thank you!!!
You need to add and import the word.js script:
import Highcharts from "highcharts/highmaps";
import customWord from "./word.js";
Live demo: https://codesandbox.io/s/v0x5zx6q05
Also, I can recommend you to use highcharts-react-official wrapper: https://www.npmjs.com/package/highcharts-react-official