I'm making a dashboard using node.js, socket.io and highcharts.
I want my chart to update dynamically on receiving a request from socket.io.
How do I do that?
Here is my client side code,
<script>
var socket=io();
var sales=0;
var e1tcount=0;
var e1scount=0;
var e2tcount=0;
var e2scount=0;
$(function () {
$('#container').highcharts({
chart: {
type: 'column',
events: {
load: function(){
socket.on('response', function(arr){
$('#c1').html(arr[0]);
sales+=arr[2];
$('#sales').html(sales);
if(arr[1]=="CT")
{
e1tcount++;
$('#e1t').html(e1tcount);
e1scount+=arr[2];
$('#e1s').html(e1scount);
//Tried this, doesnt work
var sold=chart.series[0].data[0].y;
chart.series[0].data[0].update(sold+1);
}
if(arr[1]=="AB")
{
e2tcount++;
$('#e2t').html(e2tcount);
e2scount+=arr[2];
$('#e2s').html(e2scount);
}
});
}
}
},
title: {
text: 'Stacked column chart'
},
xAxis: {
categories: ['Event 1', 'Event 2']
},
yAxis: {
min: 0,
title: {
text: 'Total fruit consumption'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
}
}
},
legend: {
align: 'right',
x: -70,
verticalAlign: 'top',
y: 20,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
formatter: function() {
return '<b>'+ this.x +'</b><br/>'+
this.series.name +': '+ this.y +'<br/>'+
'Total: '+ this.point.stackTotal;
}
},
colors: ['#FFCC99','#3366CC'],
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black, 0 0 3px black'
}
}
}
},
series: [{
name: 'Sold',
data: [0, 0]
}, {
name: 'Available',
data: [20, 20]
}]
});
});
</script>
Intially the number of tickets is 20, but as soon as I post data using curl, the number of sold tickets should increase and available tickets should decrease, depending on the event.
How do I go about?
And what error do you have in console? ;) I guess somethng about 'cannot read property of udefined'. You need to create chart variable first, then you can use that variable, right? So:
$('#container').highcharts({
chart: {
type: 'column',
events: {
load: function(){
var chart = this; // create chart variable for future use
socket.on('response', function(arr){
Then that code should work:
//Tried this, doesnt work
var sold=chart.series[0].data[0].y;
chart.series[0].data[0].update(sold+1);
If this won't be enough, then copy&paste errors here.
Related
I'm using angular6 and the office highcharts angular wrapper (https://github.com/highcharts/highcharts-angular) in version 2.4.0.
Everything is working good except the labels that can be placed manually on the chart.
I just don't manage to get them refreshed.
I have a graph that can be filtered through the back-end. When I get the new generated data I update the data in my chartOptions which are used in the html as [options]="chartOptions" for the angular component.
The chartOptions are instanciated from my default class like this: this.chartOptions = new GraphConfigSeqAtteptedVsAchived().chartOptions;
The Class looks like this:
`import * as Highcharts from 'highcharts';
export class GraphConfigSeqAtteptedVsAchived{
public chartOptions: any;
constructor(){
this.chartOptions = {
chart: {
height: 600,
width: 620,
events:{load: function(){
this.myTooltip = new Highcharts.Tooltip(this, this.options.tooltip);
}
},
backgroundColor: 'transparent',
},
credits: {
text: 'charname',
href: ''
},
labels: {
},
exporting:{
chartOptions:{
title: {
text:'not set',
style:{
color: '#000000'
},
margin: 0
}
}
},
tooltip: {
enabled: false
},
xAxis: {
min: -10,
max: 0,
width: 535,
title: {
text: 'Rainfall (mm)',
style: {
fontWeight: "bold",
color: '#000000'
}
},
reversed: true,
tickInterval: 1
},
yAxis: {
min: -10,
max: 0,
title: {
text: 'Rainfall (mm)',
style: {
fontWeight: "bold",
color: '#000000'
}
},
reversed: true,
tickInterval: 1
},
title: {
text: ' as',
margin: 0,
style:{
color: 'transparent'
}
},
legend: {
layout: 'vertical',
align: 'left',
verticalAlign: 'top',
x: 100,
y: 90,
floating: true,
backgroundColor: '#FFFFFF',
borderWidth: 1,
navigation: {
enabled: false
}
},
plotOptions: {
scatter: {
stickyTracking: false,
allowPointSelect: true,
marker: {
radius: 3,
states: {
hover: {
enabled: true,
lineColor: 'rgb(100,100,100)'
}
}
},
tooltip: {
headerFormat: '<b>{series.name}</b><br>',
pointFormat: '',
hideDelay: 500
},
symbol: "circle",
events: {
mouseOut: function() {
this.chart.myTooltip.hide();
this.chart.myTooltip.options.enabled = false;
},
mouseOver: function() {
if(this.halo) {
this.halo.attr({
'class': 'highcharts-tracker'
}).toFront();
}
},
click: function(event) {
this.chart.myTooltip.options.enabled = true;
this.chart.myTooltip.refresh(event.point, event);
}
}
},
line:{
events: {
legendItemClick: function () {
return false;
}
}
},
series:{
allowPointSelect: true
}
},
series: [{
type: 'line',
data: [[30, 30], [-30, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 1
}
},
showInLegend: false,
color: "rgba(0, 0, 0, 0.6)",
enableMouseTracking: false
},{
type: 'line',
data: [[30, 29.5], [-29.5, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(125, 162, 159, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[29.5, 30], [-30, -29.5]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(125, 162, 159, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[29, 30], [-30, -29]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(197, 144, 161, 0.35)",
enableMouseTracking: false
},{
type: 'line',
data: [[30, 29], [-29, -30]],
marker: {
enabled: false
},
states: {
hover: {
lineWidth: 0
}
},
showInLegend: false,
dashStyle: 'ShortDash',
color: "rgba(197, 144, 161, 0.35)",
enableMouseTracking: false
}]
}
}
}`
The generated data is in the typescript dynamically added and all of that works just fine. Everything is refreshed after I set the updateFlag for [(update)]="updateGraph". The axis titles are also fine.
The only thing that's not refreshed for me are the labels i dynamically added to the chart like this:
let labelTotalNumberOfEyes = {
html : this.translate.instant('GRAPHS.TOTAL_NUMBER_OF_EYES') + ': ' + (scatterSeries.data.length + scatterSeriesRetreatment.data.length),
style : {
left : '330px',
top : '368px',
fontSize : '14px',
backgroundColor: '#FFFFFF',
borderWidth: 1,
}
}
this.chartOptions.labels.items.push(labelTotalNumberOfEyes);
Does someone have any idea what I'm might doing wrong? I don't see any further option than just setting this update flag and it works fine for everything but the labels.
Please let me know if you're missing any information.
Thanks in advance.
This issue is actually a Highcharts bug. I've reported it here: https://github.com/highcharts/highcharts/issues/10429.
As a workaround, you can use Highcharts.SVGRenderer to add a custom label and store reference to it in your component. Next, when updating, call attr() method on the label (Highcharts.SVGElement) and set new options. Check demo and code posted below.
Add a label on chart callback:
constructor() {
const self = this;
this.chartCallback = chart => {
self.chart = chart;
self.label = chart.renderer
.text("test", 100, 100)
.css({
fill: "#555",
fontSize: 16
})
.add()
.toFront();
};
}
call attr() on the label while updating:
update_chart() {
const self = this,
chart = this.chart;
chart.showLoading();
setTimeout(() => {
chart.hideLoading();
self.chartOptions.series = [
{
data: [10, 25, 15],
name: "updatedSerieName"
}
];
self.chartOptions.yAxis.title.text = "updatedData";
self.label
.attr({
text: "test1",
x: 150,
y: 120
})
.css({
fill: "red",
fontSize: 20
});
self.updateFromInput = true;
}, 2000);
}
Demo:
https://codesandbox.io/s/8zo0yvqqwj
API reference:
https://api.highcharts.com/class-reference/Highcharts.SVGRenderer#text
https://api.highcharts.com/class-reference/Highcharts.SVGElement#attr
I have a chart with values that are uneven, ie: First value is 1315 and second value is 1, and so on, and when displaying the chart the labels are being overlapped. I have already searched in multiple forums but no one had the exact same problem. Here's a fiddle to see the problem: http://jsfiddle.net/6LutjLc3/
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Stacked column chart'
},
xAxis: {
categories: ['Issue']
},
yAxis: {
min: 0,
title: {
text: 'Total fruit consumption'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
}
}
},
legend: {
align: 'right',
x: -30,
verticalAlign: 'top',
y: 25,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
formatter: function () {
return '<b>' + this.x + '</b><br/>' +
this.series.name + ': ' + this.y + '<br/>' +
'Total: ' + this.point.stackTotal;
}
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
}
},
series: [{
name: 'John',
data: [1235]
}, {
name: 'Toto',
data: [2]
}, {
name: 'Matt',
data: [1]
}, {
name: 'Jane',
data: [72]
}, {
name: 'Joe',
data: [3]
}]
});
});
What I need to code is to give the value 1 one box where the label fits in as like any other example in highchart.
Thanks in advance for your help!
If your client demands use of this style of graph you can get better looking results by modifying your dataLabel settings. Setting overflow to false and adding a formatter function that only returns a value if it's let's say at least 7% (or whatever percentage works best for you) of the current total will help. See the following:
dataLabels: {
enabled: true,
overflow: false,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
},
formatter: function () {
if (this.percentage >= 7) return this.y;
}
}
Fiddle here: http://jsfiddle.net/6LutjLc3/4/
I'm working with a stacked column chart from jQuery HighCharts like this. (fiddle link)
$(function () {
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: 'Stacked column chart'
},
xAxis: {
categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas']
},
yAxis: {
min: 0,
title: {
text: 'Total fruit consumption'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
}
}
},
legend: {
align: 'right',
x: -70,
verticalAlign: 'top',
y: 20,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColorSolid) || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
formatter: function() {
return '<b>'+ this.x +'</b><br/>'+
this.series.name +': '+ this.y +'<br/>'+
'Total: '+ this.point.stackTotal;
}
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black, 0 0 3px black'
}
}
}
},
series: [{
name: 'John',
data: [null, null, 4, 7, 2]
}, {
name: 'Jane',
data: [null,null, 3, 2, 1]
}, {
name: 'Joe',
data: [3, 4, 4, 2, 5]
}]
});
});
As you can see there's the total number above each column of the total of the number. But I need something else. I want my first bar to be 100%. And then I need to calculate the percentage of the other 2 columns and place those numbers above the column. How can I make them dynamic?
What you can do here is extend the point properties and add in a new field that gets shown in the dataLabels. You would need to preprocess what these percentages would be of course. The following example assumes you have already calculated the percentage values:
plotOptions: {
series: {
dataLabels: {
enabled: true,
format: '{point.perc} %'
}
}
}
The point.perc is the point's perc property we made up and it contains the percentage values you calculated already.
In the data array you can have this format:
series: [{
data: [{y: 29.9, perc: 100}, {y:71., perc: 50}, {y:106.4, perc: 30}, {y:129.2, perc: 20}]
}]
Live demo here.
I have this chart: http://jsfiddle.net/bfqe5/1/
var rangeStart = {1386828000000: 1333256400000}; //used in the tooltip
var rangeEnd = {1386828000000: 1364706000000}; //used in the tooltip
$('#container').highcharts({
chart: {
type: 'column'
},
title: {
text: null
},
marginBottom: 25,
xAxis: {
type: 'datetime',
lineColor: '#000000',
lineWidth: 2,
labels: {
formatter: function () {
var myDate = new Date(this.value);
var newDateMs = Date.UTC(myDate.getUTCFullYear(), myDate.getUTCMonth(), myDate.getUTCDate());
return Highcharts.dateFormat('%m/%Y', newDateMs);
},
style: {
fontWeight: 'bold',
color: '#000000'
}
},
title: {
text: 'Date',
style: {
color: 'black',
fontWeight: 'normal',
fontSize: '16px'
}
}
},
yAxis: {
title: {
text: null
},
labels: {
format: '{value} %',
step: 1,
style: {
fontWeight: 'bold',
color: '#000000'
}
},
allowDecimals: false,
endOnTick: false,
lineColor: '#000000',
lineWidth: 2,
gridLineColor: null,
minorGridLineWidth: 0,
minorTickInterval: 2,
minorTickColor: '#000000',
minorTickLength: 5,
minorTickWidth: 2,
minorTickPosition: 'inside'
},
tooltip: {
formatter: function () {
var start = new Date(rangeStart[this.x]);
var end = new Date(rangeEnd[this.x]);
var startDate = Date.UTC(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate());
var endDate = Date.UTC(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate());
return '<span style="font-size:14px; font-weight:bold;">' + this.series.name + ': ' + this.y + ' %</b></span><br /><span style="color:#2F7ED8;">Date range</span><br />' + Highcharts.dateFormat('%m/%e/%Y', start) + ' - ' + Highcharts.dateFormat('%m/%e/%Y', end);
},
valueSuffix: '%'
},
legend: {
symbolPadding: 10,
itemDistance: 23,
symbolWidth: 50,
backgroundColor: '#FFFFFF',
align: 'center',
borderWidth: 0,
verticalAlign: 'bottom',
floating: false,
width: 600
},
series: [{
name: 'data1',
data: [
[1386828000000, 1]/*,
[1389228000000, 0]*/ //if uncommented, mostly works
],
color: '#a3052c'
}, {
name: 'data2',
data: [
[1386828000000, 3]/*,
[1389228000000, 0]*/ //if uncommented, mostly works
],
color: '#faa738'
}, {
name: 'data1',
data: [
[1386828000000, 6]/*,
[1389228000000, 0]*/ //if uncommented, mostly works
],
color: '#002144'
}, {
name: 'data4',
data: [
[1386828000000, 3]/*,
[1389228000000, 0]*/ //if uncommented, mostly works
],
color: '#008001'
}]
});
It should be displaying with 4 individual columns (like http://jsfiddle.net/VrgW7/), but for some reason they all seem to be stacking on top of each other. I can get it to display mostly correct by using a hack (uncomment [1389228000000, 0] in each series), but then it's behaving like there are 2 sets of data.
I've tried adding:
plotOptions: {
column: {
stacking: 'null' //or 'normal' or 'percent'
}
},
but it didn't change the display as I want it to (it actually stacked them... I guess as expected).
Everything is correct in my jsfiddle (data values, tooltip) except the way it's displaying. Does anybody have any suggestions on what to change to get it to display correctly?
The issue here has to do with scaling I think. If we change all your timestamps to 15 we get individual columns. So, what I did was set the xAxis.min and xAxis.max to be just outside of the dates of your data:
xAxis: {
type: 'datetime',
min: 1386827000000,
max: 1389229000000,
....
See this jsFiddle.
I would like to set all colors for the pie slices to gray except for the color pie slice clicked in the legend. I have been only able to figure out how to change the color of only the clicked legend item not the others.
I have tried setting id's to the data points and using e.target but that didn't provide the proper access.
Thanks for your help.
Here is my myFiddle.
$(document).ready(function () {
var myCharts = {
chart: {
renderTo: 'container',
type: 'pie',
backgroundColor: 'rgba(255, 255, 255, 0.1)',
borderColor: 'rgba(255, 255, 255, 0.1)',
margin: [38, 20, 20, 20],
width: 300,
height: 300,
shadow: true,
},
colors: [
'#0066FF',
'#33CC33',
'#FF0000',
'#FFFF00',
],
credits: {
enabled: false
},
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'top',
x: 0,
y: 0
},
title: {
text: 'Net Activations',
verticalAlign: 'bottom',
y: 10
},
subtitle: {
text: '7%',
verticalAlign: 'middle',
y: 30,
style: {
color: 'black',
fontSize: '40px'
}
},
yAxis: {
tickColor: '#FF0000',
tickWidth: 3,
tickInterval: 5
},
xAxis: {
tickColor: '#FF0000',
tickWidth: 3,
tickInterval: 5
},
plotOptions: {
pie: {
states: {
hover: {
enabled: false
}
},
point: {
events: {
legendItemClick: function () {
this.graphic.attr({
fill: '#CCCCCC'
});
return false
}
}
},
dataLabels: {
enabled: true,
distance: 0.1,
color: 'black',
formatter: function () {
return '<b>' + this.point.name + '</b>: ' + this.percentage + ' %';
},
},
innerSize: '60%',
shadow: true,
size: '100%',
allowPointSelect: true,
slicedOffset: 10,
}
},
tooltip: {
enabled: false
},
series: [{
data: [],
showInLegend: true,
}]
};
myCharts.chart.renderTo = 'container';
myCharts.title.text = 'Net Activations';
var actual = 52,
goal = 73 - actual,
ATB = 100 - goal - actual;
myCharts.series[0].data = [{
name: 'Actual',
y: actual,
id: 0
}, {
name: 'goal',
y: goal,
id: 1
}, {
name: 'ATB',
y: ATB,
id: 2
}];
new Highcharts.Chart(myCharts);
});
Instead of attacking the SVG directly with this.graphic.attr, you'd be better off using the API to update the slice. Point.update works well for this:
legendItemClick: function () {
var series = this.series;
for (var i=0; i < series.data.length; i++){
var point = series.data[i];
if (point == this){
// set back to old color
point.update({color: series.chart.options.colors[this.id]});
}else{
// make it gray
point.update({color: '#CCCCCC'});
}
}
return false;
}
Updated fiddle here.
Well, I can get you 50% of the way there:
point: {
events: {
click: function () {
if (event.point.sliced) {
$.each(this.series.data, function (i, e) {
if (!e.sliced) {
this.graphic.attr({
fill: '#CCCCCC'
});
}
});
}
}
}
This still breaks when you re-click the slice to slot it back in - the colors of the other slices are still grey until you click on them. Will need to look more into this.