I have an angularjs application and I use highcharts-ng to display some graphs. I got the data from an http request and I modify the data to display them in the graph.
If I receive just a just some data, it's ok, it works but when I receive a lot of data, the highcharts component take a long time to display the data. And in this time the browser in freezed.
In my controller, I have:
widgetCtrl.chartDataLine = {
chart: {
type: 'spline',
zoomType: 'xy'
},
title: {
text: 'Power Consumption'
},
xAxis: {
type: 'datetime',
title: {
text: 'Date time'
}
},
yAxis: {
title: {
text: 'Power (W)'
},
min: 0
},
options: {
exporting: {
enabled: true,
allowHTML: true,
buttons: {
contextButton: {
menuItems: null,
onclick: function () {
this.exportChart();
}
}
}
}
},
plotOptions:{
series:{
turboThreshold:60000 // larger threshold or set to 0 to disable
}
},
series: []
};
I call the data like that:
var getMetrics = function (searchText) {
var param = {
target: searchText
};
xcsDatasource.get('beo').callAction('getPowerMetrics', param.target, widgetCtrl.pickerFrom.date, widgetCtrl.pickerTo.date).then(
function (result) {
widgetCtrl.metrics = result;
widgetCtrl.metricsAsString = JSON.stringify(result);
var t = getChartData(result);
widgetCtrl.chartDataLine.series = angular.copy(t);
console.log(widgetCtrl.chartDataLine);
//widgetCtrl.chartDataArea.series = getChartData(result);
},
function () {
widgetCtrl.metrics = {};
widgetCtrl.metricsAsString = '';
}
);
};
And this is my html:
<div ng-if="config.linechart" class="graph-container col-sm-12">
<highchart id="chart1" config="pwrDashboardViewCtrl.chartDataLine"></highchart>
</div>
Do you have an idea how I can do to continue to load the grapph without freezing the browser ?
Related
I am new to React and I work on a small project basically, I have a chart and I just want to display the current value from the chart.
For example, I have a chart with 4 random values:[5,2,5,1], so I want to have displayed the current value below the chart like first is 5, second is 2 and so on.
Here is my part of code:
class App extends React.Component {
addPoint = (point) => {
currentData = this.state.options.series[0].data
this.setState({
options: {
series: [
{ data: [...currentData, point]}
]
}
});
}
constructor(props) {
super(props);
this.internalChart = undefined;
this.dataStream = new DataStream();
this.dataStream.setUpdateCallback(this.addPoint);
this.state = {
options: {
chart: {
events: {
load: function () {
}
}
},
time: {
useUTC: false
},
rangeSelector: {
buttons: [{
count: 1,
type: 'minute',
text: '1M'
}, {
count: 5,
type: 'minute',
text: '5M'
}, {
type: 'all',
text: 'All'
}],
inputEnabled: false,
selected: 2
},
title: {
text: 'Live random data'
},
exporting: {
enabled: false
},
series: [{
name: 'Random data',
data: [[(new Date()).getTime(), 0]]
}]
}
};
}
render() {
return (
<HighchartsReact
constructorType={"stockChart"}
highcharts={Highcharts}
options={this.state.options}
/>
);
}}
Based on your snippet code, try to make random method by using function
Math.floor(Math.random() * Math.floor(max))
And then assign the options to HighchartsReact,
Full code on sandbox: https://codesandbox.io/s/headless-dream-0lzj1
So, I have been trying to use drill down to multiple levels, problem I am facing is that I couldn't drill down to the third level because the data will be fetched by ajax upon second drilldown's selection.
for example, refer to this link:
https://codepen.io/ajaymalhotra15/pen/aZpxXq
drilldown example
Here, the third level is possible because he has the data already, but mine will be depended on seconds selection.
So, how to make this happen, where am I supposed to call the ajax request and set the drill down series data dynamically?
EDIT:
Highcharts.chart("energy_chart", {
chart: {
type: "column",
spacingBottom: 15,
spacingTop: 10,
spacingLeft: 10,
spacingRight: 10,
backgroundColor: "#f2f2f2",
events: {
load: function() {
var fin = new Date();
var finDate = fin.getDate();
var finMonth = fin.getMonth();
var finYear = fin.getFullYear();
var ini = new Date();
ini.setFullYear(ini.getFullYear() - 1);
var iniDate = ini.getDate();
var iniMonth = ini.getMonth();
var iniYear = ini.getFullYear();
if (this.yAxis[0].dataMax == 0) {
this.yAxis[0].setExtremes(null, 1);
}
//this.yAxis.set
this.xAxis[0].setExtremes(
Date.UTC(iniYear, iniMonth, iniDate),
Date.UTC(finYear, finMonth, finDate)
);
},
drilldown: function(e) {
var charts_this = this;
var inidrillDate = new Date(e.point.x);
setTimeout(function() {
inidrillDate.setDate(0);
inidrillDate.setMonth(inidrillDate.getMonth());
var DateinidrillDate = inidrillDate.getDate();
var MonthinidrillDate = inidrillDate.getMonth();
var YearinidrillDate = inidrillDate.getFullYear();
var findrillDate = inidrillDate;
findrillDate.setMonth(findrillDate.getMonth() + 1);
findrillDate.setDate(findrillDate.getDate() - 1);
var DatefindrillDate = findrillDate.getDate();
var MonthfindrillDate = findrillDate.getMonth();
var YearfindrillDate = findrillDate.getFullYear();
charts_this.xAxis[0].setExtremes(
Date.UTC(
YearinidrillDate,
MonthinidrillDate,
DateinidrillDate
),
Date.UTC(
YearfindrillDate,
MonthfindrillDate,
DatefindrillDate
)
);
if (charts_this.yAxis[0].dataMax === 0) {
charts_this.yAxis[0].setExtremes(null, 1);
}
}, 0);
}
}
},
title: {
text: '<p className="energy_gen">Energy Generated</p>'
},
exporting: { enabled: false },
xAxis: {
type: "datetime",
labels: {
step: 1
},
dateTimeLabelFormats: {
day: "%e"
}
},
yAxis: {
title: {
text: "kWh"
}
},
credits: {
enabled: false
},
plotOptions: {
series: {
cursor: "pointer",
dataLabels: {
enabled: true,
format: "{point.y}"
},
color: "#fcd562",
point:{
events:{
click:function(event){
if(this.options!=null){
var dayOfYear=new Date(this.x).getFullYear() +"-"+(new Date(this.x).getMonth()+1)+"-"+new Date(this.x).getDate();
var formatted_date = new Date(this.x).getDate() + " " + months[(new Date(this.x).getMonth())] +" "+ new Date(this.x).getFullYear();
// document.getElementById('chart_date_id').innerHTML = formatted_date; //setting modal title with current date
$('#container').bind('mousemove touchmove touchstart', function (e) {
var chart,
point,
i,
event;
var sync_charts = $('.chart');
for (i = 0; i < sync_charts.length; i = i + 1) {
var chart_1 = sync_charts[i];
var chart_2 = chart_1.getAttribute('data-highcharts-chart');
chart=Highcharts.charts[chart_2];
event = chart.pointer.normalize(e.originalEvent);
point = chart.series[0].searchPoint(event, true);
if (point) {
point.highlight(e);
}
}
});
Highcharts.Pointer.prototype.reset = function () {
return undefined;
};
Highcharts.Point.prototype.highlight = function (event) {
event = this.series.chart.pointer.normalize(event);
this.onMouseOver(); // Show the hover marker
this.series.chart.tooltip.refresh(this); // Show the tooltip
this.series.chart.xAxis[0].drawCrosshair(event, this); // Show the crosshair
};
function syncExtremes(e) {
var thisChart = this.chart;
if (e.trigger !== 'syncExtremes') { // Prevent feedback loop
Highcharts.each(Highcharts.charts, function (chart) {
if (chart !== thisChart) {
if (chart.xAxis[0].setExtremes) { // It is null while updating
chart.xAxis[0].setExtremes(
e.min,
e.max,
undefined,
false,
{ trigger: 'syncExtremes' }
);
}
}
});
}
}
axios({
url: config.fvcstat,
method: "POST",
data: {
"customerId":self.props.location.state.detail.customerId,"rmsVendorId":self.props.location.state.detail.rmsVendorId,
"date":dayOfYear,
"powerType":self.props.location.state.detail.powerType
},
headers: {
"Content-Type": "application/json"
}
}).then((res)=>{
let activity = fvc.data;
if($('.chart')){
$('.chart').remove();
}
$.each(activity.datasets, function (i, dataset) {
console.log(1)
var chartDiv = document.createElement('div');
chartDiv.className = 'chart';
document.getElementById('container').appendChild(chartDiv);
Highcharts.chart(chartDiv,{
chart: {
},
plotOptions: {
series: {
marker:{
enabled:false
}
}
},
exporting: { enabled: false },
title: {
text: dataset.name,
align: 'left',
margin: 0,
x: 30
},
credits: {
enabled: false
},
legend: {
enabled: false
},
xAxis: {
crosshair:{ width: 3},
events: {
setExtremes: syncExtremes
},
labels: {
format: '{value}'
},categories: activity.xData
},
yAxis: {
title: {
text: null
}
},
series: [{
data: dataset
}],
tooltip: {
positioner: function () {
return {
x: this.chart.chartWidth - this.label.width,
y: 10 // align to title
};
},
borderWidth: 0,
backgroundColor: 'none',
pointFormat: '{point.y}',
headerFormat: '',
shadow: false,
style: {
fontSize: '18px'
},
valueDecimals: dataset.valueDecimals
},
series: [{
data: dataset.data,
name: dataset.name,
type: dataset.type,
color: Highcharts.getOptions().colors[i],
fillOpacity: 0.3,
tooltip: {
valueSuffix: ' ' + dataset.unit
}
}]
});
});
})
}
}
}
}
}
},
tooltip: {
formatter: function() {
if (this.point.options.drilldown) {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%b %Y", new Date(this.x))
);
} else {
return (
"Energy generated: <b> " +
this.y +
"</b> kWh " +
"<br>" +
Highcharts.dateFormat("%e %b %Y", new Date(this.x))
);
}
}
},
series: [{'data':obj.data,'name':obj.name,"color":"#4848d3"}],
drilldown: {
series: obj.data
}
});
So, here if you notice in plotoptions i am trying to create a whole new chart which is a synced line charts showing frquency, voltage and current.
But, i am guessing my approach is not correct as i am plotting a new highchart.
So, how do i make this synced line chart part of my drilldown.
let me know if you require any help in understanding.
I will suggest first minimize the plotoption. Then expand for further fuck up :P
Thanks.
You can put all your logic to get the third level data and to create a drilldown series in drilldown event:
chart: {
type: 'column',
events: {
drilldown: function(e) {
if (!thirdLevel.length) {
// get data
}
if (!e.seriesOptions) {
var chart = this,
drilldowns = {
'Animals': {
name: 'Animals',
data: [
['Cows', 2],
['Sheep', 3]
]
},
'Fruits': {
name: 'Fruits',
data: [
['Apples', 5],
['Oranges', 7],
['Bananas', 2]
]
},
'Cars': {
name: 'Cars',
data: [
['Toyota', 1],
['Volkswagen', 2],
['Opel', 5]
]
}
},
series = drilldowns[e.point.name];
chart.addSingleSeriesAsDrilldown(e.point, series);
chart.applyDrilldown();
}
}
}
}
Live demo : http://jsfiddle.net/BlackLabel/86v3L4ft/
API Reference: https://api.highcharts.com/highcharts/chart.events.drilldown
I have an angularjs application with the library Highcharts.
In my application I have some graphs and want to export the data of the graphs in a CSV file. So I used the exporting function from highcharts but I have a problem with the dates. On my graphs, I display the dates with a format "MM/DD/YYYY hh:mm:ss" so when I export my data, I have the same format but I would have the dates in milliseconds. I try to change teh 'dateFormat' field in the exporting options but milliseconds is not part of the accepted formats.
This is my graph options:
widgetCtrl.chartDataLine = {
chart: {
type: 'spline',
zoomType: 'x',
isZoomed: false,
marginTop:55
},
title: {
text: 'title',
align:'left',
x: 20,
y: 18,
style: {
fontSize: '14px'
}
},
xAxis: {
type: 'datetime',
title: {
text: 'Date time'
}
},
yAxis: {
title: {
text: 'Power (W)'
}
},
boost: {
enabled: true
},
exporting: {
fallbackToExportServer: false,
enabled: true,
allowHTML: true,
filename: 'myFile',
menuItemDefinitions: {
downloadJSON: {
onclick: function () {
downloadJSON('myFile.JSON', widgetCtrl.chartDataLine.series);
},
text: 'Download JSON'
}
},
csv: {
decimalPoint: '.',
dateFormat: '%Y-%m-%d %H:%M:%S'
},
buttons: {
contextButton: {
menuItems: [
'printChart',
'downloadPNG',
'downloadJPEG',
'downloadPDF',
'downloadSVG',
'downloadCSV',
'downloadJSON'
]
}
}
},
navigation: {
buttonOptions: {
align: 'left'
}
}
series: [...]
};
Do you have an idea how I coul do that without writing myself teh csv file but using the highcharts functions ?
Unfortunately, Highcharts does not have the default option to export data in milliseconds (timestamp). However, it can be done easily by wrapping Highcharts.Chart.prototype.getDataRows method and map the data array which is used for export.
(function(H) {
H.wrap(H.Chart.prototype, 'getDataRows', function(proceed, multiLevelHeaders) {
var rows = proceed.call(this, multiLevelHeaders);
rows = rows.map(row => {
if (row.x) {
row[0] = row.x;
}
return row;
});
return rows;
});
}(Highcharts));
Demo:
https://jsfiddle.net/BlackLabel/5p1nvq37/
$scope.hhData = $firebaseArray(aRef.orderByChild('ts').startAt('2018-07-02').limitToLast(50));
$scope.hhData.$loaded().then(function () {
$scope.chartSeriesYArray = [];
$scope.chartSeriesXArray = [];
$scope.hhData.$watch(function() {
angular.forEach($scope.hhData, function (value) {
$scope.chartSeriesXArray.push(moment(value.ts).format('MMMM Do YYYY, h:mm:ss'));
$scope.chartSeriesYArray.push(parseFloat(value.ein));
});
Highcharts.chart('chart', {
chart: {
type: 'column'
},
title: {
text: 'Half Hourly Energy Consumption'
},
xAxis: {
categories: $scope.chartSeriesXArray,
crosshair: true
},
yAxis: {
min: 0,
title: {
text: 'Energy (kwh)'
}
},
series: [{
name: 'Meter01',
data: $scope.chartSeriesYArray
}]
});
});
});
I wanted to be able to update the data live from Firebase into the chart. However, this code only allows the data to be updated once at the start and it never updates itself again. Is there any way to work around this? - tried the $interval method as suggested but it didn't work :(
Update: I managed to do so with $watch. However, this now means that my graph will only show when there is data coming in. If there is no data coming in (EG: when you just turn on the system) then no graph will show at all. This there another way around this
Try using the $interval service to retrieve your data after a certain amount of time.
$interval(function() {
$scope.hhData.$loaded().then(function () {
$scope.chartSeriesYArray = [];
$scope.chartSeriesXArray = [];
angular.forEach($scope.hhData, function (value) {
$scope.chartSeriesXArray.push(moment(value.ts).format('MMMM Do YYYY, h:mm:ss'));
$scope.chartSeriesYArray.push(parseFloat(value.ein));
});
Highcharts.chart('chart', {
chart: {
type: 'column'
},
title: {
text: 'Half Hourly Energy Consumption'
},
xAxis: {
categories: $scope.chartSeriesXArray,
crosshair: true
},
yAxis: {
min: 0,
title: {
text: 'Energy (kwh)'
}
},
series: [{
name: 'Meter01',
data: $scope.chartSeriesYArray
}]
});
});
}, 5000); //this function will run every 5 seconds
*don't forget you also need to add the $interval parameter to your controller, and the interval goes by milliseconds
app.controller('myCtrl',["$scope", "$interval", function($scope, $interval) {
//code goes here
}]);
I'm working on a project which uses Highcharts and Angularjs and fetches data using SignalR. The problem is the pie chart initializes correctly but can not update the diagram with the data comes from server. here is my code:
'use strict';
angular.module('mbCharts').directive('mbGauge', [
'mbWebMetricsService',
function (mbWebMetricsService) {
return {
//
// scope is inherited from the widget using this directive
//
templateUrl: '/ext-modules/mbCharts/mbGaugeTemplate.html',
link: function (scope, el, attrs) {
Highcharts.chart(el[0], {
chart: {
type: 'pie'
},
title: {
text: scope.title
},
plotOptions: {
pie: {
allowPointSelect: true,
cursor: 'pointer',
dataLabels: {
enabled: true,
format: '<b>{point.name}</b>: {point.percentage:.1f} %'
}
}
},
series: [{
data: [{
name: "Microsoft Internet Explorer",
y: 100
}, {
name: "Chrome",
y: 0,
sliced: true,
selected: true
}]
}]
});
// locate the widget showing this gauge
var widget = el.closest('.gridster-item');
// monitor the widget's size
widget.resize(function () {
//if (scope.chart != null) {
// scope.chart.chartWidth = widget.width();
// scope.chart.chartHeight = widget.height();
//}
});
//scope.title = mbWebMetricsService.getTitleForMetric(scope.metric);
scope.title = "CPU percentage";
scope.initialized = false;
scope.$on('mbWebMetricsService-received-data-event', function (evt, data) {
var val = Math.round(data[scope.metric]);
scope.chart.series[0].data[0].y = val;
scope.chart.series[0].data[1].y = 100 - val;
});
}
};
}
]);
The problem is how you want to update data. It's not about changing options in chart object, but using proper API. To update points, use chart.series[index].data[pointsIndex].update().
So in your case, first store a chart object:
var myChart = new Highcharts.Chart(el[0], { ... });
Then update points:
scope.$on('mbWebMetricsService-received-data-event', function (evt, data) {
var val = Math.round(data[scope.metric]);
myChart.series[0].data.update(val);
myChart.series[0].data.update(100 - val);
});