How to use Vue bound data in Highcharts? - highcharts

I have a chart which axis contents and data will change with time. This happens in a Vue framework.
My idea was to use setData() and setCategories() to give the chart pointers to data which will be dynamic. This does not work: when data is updated, the chart is not.
The example is in Codepen.io and reproduced below for reference. Please note that this hangs my browser (but the codepen version is OK)
new Vue({
el: "#app",
data: {
config: {
chart: {
type: 'heatmap'
},
series: [{}]
},
src: ['a', 'b'],
dst: ['x', 'y'],
data: [[0, 0, 1], [0, 1, 2], [1, 0, 3], [1, 1, 4]],
chart: undefined
},
mounted() {
this.chart = Highcharts.chart('container', this.config)
console.log(this.chart)
// the content of the axis and the data will be dynamic
this.chart.series[0].setData(this.data, true)
this.chart.xAxis[0].setCategories(this.src, true)
this.chart.yAxis[0].setCategories(this.dst, true)
// simulation of some data changing after some time
setTimeout(() => {
this.data = [[0, 0, 10], [0, 1, 20], [1, 0, 30], [1, 1, 40]]
// console.log('updated')
}, 3000)
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/6.0.6/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/heatmap.js"></script>
<div id="app">
<div id="container">
</div>
</div>

To allow the chart to automatically update, you need to watch the vue variables using a watcher or by using them in the view.
I'm using the following steps:
Since Highcharts requires a real element on the page before it works, we cannot use them directly inside the view, and we need to use a watcher on the fields that can change.
We first extract our chart render code into a seperate function, this allows us to call the method from multiple places.
Then we add watchers for the variables our chart requires, and inside those watchers, we call our render function.
Finally, we render our chart inside the mounted method.
From this point, we can see that the library we use, Highcharts also supports updating the data dynamically on changes, we can use this in our advantage to prevent re rendering the full element, and save some CPU cycles here.
new Vue({
el: "#app",
data: {
chart: undefined,
config: {
chart: {
type: 'heatmap'
},
series: [{}]
},
src: ['a', 'b'],
dst: ['x', 'y'],
data: [[0, 0, 1], [0, 1, 2], [1, 0, 3], [1, 1, 4]]
},
mounted() {
this.render();
// simulation of some data changing after some time
setTimeout(() => {
this.data = [[0, 0, 10], [0, 1, 20], [1, 0, 30], [1, 1, 40]]
console.log('updated')
}, 3000)
},
watch: {
data() {
this.chart.series[0].setData(this.data, true)
},
config() {
this.render();
},
src() {
this.chart.xAxis[0].setCategories(this.src, true)
},
dst() {
this.chart.yAxis[0].setCategories(this.dst, true)
},
},
methods: {
render() {
this.chart = Highcharts.chart('container', this.config)
// the content of the axis and the data will be dynamic
this.chart.series[0].setData(this.data, true)
this.chart.xAxis[0].setCategories(this.src, true)
this.chart.yAxis[0].setCategories(this.dst, true)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highcharts/6.0.6/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/heatmap.js"></script>
<div id="app">
<div id="container">
</div>
</div>

Related

xAxis Image Disappears in Highcharts After First Refresh

I have a page with a variety of select menus. The select options are used in an ajax call to build a Highcharts bar graph. Every time a filter changes, the graph gets recreated. I did this instead of updating the series data, because in the past I have noticed that destroying and recreating was more efficient than updating.
I want images to show on the x-axis, so I used a nice little trick of creating two x axes, used formatter to return an image on the first axis, and linked the second axis to the first. This works on first refresh. However, every time the chart gets recreated thereafter, the image disappears. I checked my console and I don't see any errors.
And idea of what's going on here?
/**
* Whenselection changes
*/
$(document).on('change', '.filter', function(){
getChartData($params)
})
});
/**
* API call to get data that will populate charts.
* #param {obj} params
*/
function getChartData(params)
{
//Get chart data
$.ajax({
url: apiURL + '/chartdata/',
data: params,
dataType: 'json',
cache: false,
success: function(data) {
initChart(data[0]);
}
});
function initChart(chartData)
{
var chart = Highcharts.chart('container', {
chart: {
type: 'bar',
backgroundColor: 'transparent', //#E8EAF6',
height: '23%',
marginLeft: 35
},
title: {
text: null
},
xAxis: {
categories: [category1, category2],
lineColor: 'transparent',
min: 0,
tickColor: 'transparent',
title: {
text: null
},
labels: {
x: -35,
useHTML: true,
overflow: 'allow',
formatter: function () {
if(this.isFirst == true)
return '<img src="../assets/img/nat-jr-grad-gold.png"><br/>';
else
return '<img src="../assets/img/nat-jr-grad-purple.png"><br/>';
}
}
},
yAxis: {
min: 0,
title: {
useHTML: true,
text: null
},
labels: {
enabled: false
},
lineWidth: 0,
minorGridLineWidth: 0,
gridLineWidth: 0,
lineColor: 'transparent',
gridLineColor: 'transparent',
},
legend: {
enabled: false
},
series: [{
name: category1,
data: [{name: category1, y:Math.round(chartData.p_grad_nongap * 100), {y: null}],
}, {
name: category2,
data: [null, {name: category2, y: Math.round(chartData.p_grad_gap * 100)}]
}]
});
}
I reproduced your problem on a simplified example: http://jsfiddle.net/BlackLabel/sm2r684n/
For the first time, the image is loaded asynchronously and the chart does not take it into account when calculating the margins. Every next time the result is different, so you should wait until the picture is loaded:
var img = new Image();
img.onload = function() {
initChart();
}
img.src = "https://www.highcharts.com/samples/graphics/sun.png";
Live demo: http://jsfiddle.net/BlackLabel/m09ok2cg/
I think ppotaczek had the cortrect root cause of the problem; the image is loaded asynchronously and the chart does not take it into account when calculating the margins. His suggestion used setTimeout function to continuously redraw the graph, which is rather inefficient. My work-around for this was to just add the images as avg elements using chart.renderer after the chart was created.
/* Render X-Axis images */
chart.renderer.image('../assets/img/img1.png', 0, 40, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();
chart.renderer.image('../assets/img/img2.png', 0, 130, 32, 36)
.css({
class: 'icon-img',
zIndex: 10
})
.add();

Negative bar chart of highcharts in chartjs

I been playing with Chartjs to generate something like highcharts but couldn't bring it close. The application is finished and it seems a waste to buy highcharts, migrate from chartjs for this single chart.
Is there a way Chartjs can output something like that? I did try creating two horizontal bar charts side-by-side in separate canvas; while i saw the output; the behavior is not what I want.
You could use a horizontal stacked barchart with two datasets. Add negative values to the dataset on the left (male) but display the absolute value in the callbacks of the xAxis and the tooltip.
var ctx = document.getElementById('myChart').getContext('2d');
var data = {
labels: ["20-30", "10-20", "0-10"],
datasets: [{
label: "Male",
backgroundColor: "rgba(54, 162, 235, 0.2)",
borderColor: "rgb(54, 162, 235)",
borderWidth: 2,
data: [-65, -59, -20],
}, {
label: "Female",
backgroundColor: "rgba(255,99,132,0.2)",
borderColor: "rgba(255,99,132,1)",
borderWidth: 2,
data: [72, 45, 18],
},
]
};
var myBarChart = new Chart(ctx, {
type: 'horizontalBar',
data: data,
options: {
scales: {
yAxes: [{
stacked: true
}],
xAxes: [{
ticks: {
callback: function(value, index, values) {
return Math.abs(value);
}
}
}]
},
tooltips: {
callbacks: {
label: function(tooltipItems, data) {
return data.datasets[tooltipItems.datasetIndex].label + ": " + Math.abs(tooltipItems.xLabel);
}
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.1/Chart.min.js"></script>
<canvas id="myChart"></canvas>

Origin of a coordinate space

I draw a coordinate space with the following code (JSBin). What I don't understand, is why (-1.7, 0) isn't at the origin? It seems that the origin represents (-1.675, -0.25) at the moment, which is not what I want.
<!DOCTYPE html>
<html>
<body>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/highcharts-more.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="height:400px;margin:1.5em 1em;"></div>
<script>
var chart = new Highcharts.Chart({
chart: {
renderTo:'container',
type:'area'
},
credits: false,
legend: false,
title: { text:"" },
tooltip: {},
plotOptions: {
series: {
color:'rgba(156,156,156,.9)',
fillColor:'#ffffff',
lineWidth:1,
marker: {
enabled:false,
states: {
hover: {
enabled:false
}
}
}
}
},
xAxis: {
tickmarkPlacement:'on',
categories:[-1.7, -1.65, -1.6, -1.55, -1.5, -1.45, -1.4, -1.35, -1.3]
},
yAxis: {
title: { text: "x 10000", rotation: 0, y: 0, x:10, margin: -40, align: 'high'},
tickLength: 10,
tickWidth: 1,
lineWidth: 1,
tickmarkPlacement:'on',
gridLineColor:'#ffffff',
categories:[0, 0.5, 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5]
},
series:[{
data:[[1,1],[1,3],[2,3],[2,1],[1,1]]
}]
});
</script>
</body>
</html>
By default Highcharts automatically calculates extremes for both axes. If there's no value on the plot which is close to first category (1.7 in your example) it's not used to set the minimum extreme. You can use min and max properties to set extremes manually:
xAxis: {
tickmarkPlacement:'on',
categories:[-1.7, -1.65, -1.6, -1.55, -1.5, -1.45, -1.4, -1.35, -1.3],
min: 0,
max: 8
};
I think using categories in this case is a bad idea. Categories are only an information for Highcharts how to format axis labels, data labels, tooltip etc. The actual y axis values for categories are their indexes in the array. So if you create categories like this:
categories:[-1.7, -1.65, -1.6, -1.55, -1.5, -1.45, -1.4, -1.35, -1.3]
they'll be treated as Strings and their numeric values are 0, 1, 2, 3...
That means when you create a point with this options: {x: 0, y: 5} it'll be above -1.7 category - it becomes very confusing.
You should manipulate numeric values on your axes using the following options (not categories array):
https://api.highcharts.com/highcharts/yAxis.min
https://api.highcharts.com/highcharts/yAxis.max
https://api.highcharts.com/highcharts/yAxis.tickInterval

Change color Marker Geochart

I'm using Geochart and I need to set different colors for the marker :
Production Countries : red
Raw Material Countries : blue
...
On the doc it's just noticed how to change the color of the axis using colorAxis.colors property, but I don't need axis on my project.
use the second column in the data table,
in conjunction with the colorAxis.colors option,
to specify specific colors...
use two numbers to specify the color in the second column
in the following working snippet,
0='red'
1='blue'
note: the following option will hide the legend / axis scale...
legend: 'none'
google.charts.load('current', {
callback: drawMarkersMap,
packages: ['geochart'],
mapsApiKey: 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
});
function drawMarkersMap() {
var data = google.visualization.arrayToDataTable([
['Country', 'Color', 'Size'],
['US', 0, 100],
['Mexico', 1, 100],
['Canada', 1, 100],
['Brazil', 0, 100]
]);
var options = {
colorAxis: {
colors: ['red', 'blue']
},
displayMode: 'markers',
legend: 'none'
};
var chart = new google.visualization.GeoChart(document.getElementById('chart'));
chart.draw(data, options);
};
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
EDIT
to remove the color and size values from the tooltip (on hover),
add new column for custom tooltip,
either provide additional content for the tooltip,
or just a blank string ('') to hide color & size
see following working snippet...
google.charts.load('current', {
callback: drawMarkersMap,
packages: ['geochart'],
mapsApiKey: 'AIzaSyD-9tSrke72PouQMnMX-a7eZSW0jkFMBWY'
});
function drawMarkersMap() {
var data = google.visualization.arrayToDataTable([
['Country', 'Color', 'Size', {role: 'tooltip', type: 'string', p: {html: true}}],
['US', 0, 100, ''],
['Mexico', 1, 100, ''],
['Canada', 1, 100, ''],
['Brazil', 0, 100, '']
]);
var options = {
colorAxis: {
colors: ['red', 'blue']
},
displayMode: 'markers',
legend: 'none',
tooltip: {
isHtml: true
}
};
var chart = new google.visualization.GeoChart(document.getElementById('chart'));
chart.draw(data, options);
};
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="chart"></div>
note option for html tooltips...
tooltip: {
isHtml: true
}

Highcharts : Change color of series when hovering other series

I have an issue where I want to change the fill color of a column series when hovering another series. (This is to visualize related series)
I succeeded in changing the color on mouseOver, but I'm not able to restore the color on mouseOut.
The code I have until now is on jsFiddle
var hoverSerie;
var originalColor;
var newColor = '#a760d6'
$(function () {
var t = $('#container').highcharts({
chart: {
type: 'column'
},
xAxis: {
categories: ['Apples', 'Oranges', 'Pears', 'Grapes', 'Bananas']
},
yAxis: {
min: 0
},
plotOptions: {
column: {
stacking: 'normal'
}
},
series:[{
events: {
mouseOver: function() {
hoverSeries = this.chart.series[2]
originalColor = hoverSeries.options.color;
hoverSeries.options.color = newColor;
hoverSeries.update(hoverSeries.options);
},
mouseOut: function(){
if(originalColor)
{
hoverSeries.options.color = originalColor;
hoverSeries.update();
}
}
},
animation : false,
name: 'John',
data: [5, 3, 4, 7, 2]
}, {
animation : false,
name: 'Jane',
data: [2, 2, 3, 2, 1]
}, {
animation : false,
name: 'Joe',
data: [3, 4, 4, 2, 5]
}]
});
});
When hovering (mouseOver) the top stacked series (light blue) the color of the bottom (black) series. For one or other reason, the mouse out event is working differently as the mousOver event. The code appears to loop when doing a mouseOut.
Apparently when doing update inside mouseOut the redraw-part of the update causes mouseOut to be called recursively, eventually leading to "stack size exceeded". In this updated Fiddle I've used a boolean to prevent it from happening recursively, which seems to be working for me in Chrome. Hope it it what you were looking for. The only real change is the addition of the mouseWasOver variable used in both mouseOver and mouseOut.
This appears to be a bug(?) related to calling update inside mouseOut that was reported as early as 2011 on their GitHub, and 2010 on their forum.

Resources