Implementing Highcharts in RoR
Looking for the best way to implement Highcharts in RoR. I have been spoiled by ChartKick however one of the features I need in HighCharts is the Drilldowns and the maker of ChartKick said "it's a neat concept but outside the scope of ChartKick".
Has anyone worked with HighCharts and can point me in the right direction?
Maybe Chris can do an Episode or 3 on the subject... :-)
John Sanderbeck
Hey John,
If you want to just get up and running, it's really just a matter of throwing a script tag in a page and load the resources... check out the first series below for some notes on how you can load your data in
From here, it just depends on what your needs are and how many charts you needs / how you need to load the data that would determine how you would really want to implement it all for your final product.
index.html.erb
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/series-label.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="height: 400px;"></div>
<script>
Highcharts.chart('container', {
title: {
text: 'Solar Employment Growth by Sector, 2010-2016'
},
subtitle: {
text: 'Source: thesolarfoundation.com'
},
yAxis: {
title: {
text: 'Number of Employees'
}
},
legend: {
layout: 'vertical',
align: 'right',
verticalAlign: 'middle'
},
plotOptions: {
series: {
label: {
connectorAllowed: false
},
pointStart: 2010
}
},
series: [{
name: 'Installation',
data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
// here you could do <%= raw @your_data %>
// so in your controllers index action, you'd have:
// @your_data = [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
// data: <%= raw @your_data %>
// would render data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
}, {
name: 'Manufacturing',
data: [24916, 24064, 29742, 29851, 32490, 30282, 38121, 40434]
}, {
name: 'Sales & Distribution',
data: [11744, 17722, 16005, 19771, 20185, 24377, 32147, 39387]
}, {
name: 'Project Development',
data: [null, null, 7988, 12169, 15112, 22452, 34400, 34227]
}, {
name: 'Other',
data: [12908, 5948, 8105, 11248, 8989, 11816, 18274, 18111]
}],
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
}
});
</script>
I've had them working this way before, however the part I struggle with is dynamically updating the data without redrawing the entire page. I use Filterrific to filter the data.
I guess I am looking for the most efficient way to do it in RoR.
John
I can't speak to the integration with filterrific, however loading live data is just a matter of making an ajax call and updating the data:
https://www.highcharts.com/docs/working-with-data/live-data
function requestData() {
$.ajax({
url: 'url_to_your_controller_action',
success: function(point) {
var series = chart.series[0],
shift = series.data.length > 20; // shift if the series is
// longer than 20
// add the point
chart.series[0].addPoint(point, true, shift);
// call it again after one second
setTimeout(requestData, 1000);
},
cache: false
});
}
In this example, they update every second, however you could remove that part and just have the ajax function trigger when a user updates the filter attributes. From here really all you need to do is update the ajax URL with whatever params you need in order to have your controller actions query return the correct set of records based on whatever filters you selected.
That helps... I want to replace a series vs adding a point, so I assume I would have to do a chart.redraw(); ?
Check out these resources:
https://stackoverflow.com/questions/23830970/highcharts-trying-to-update-series
Based on the SO answer, you can either redraw the chart or just change out the series. I think changing the series would be the better way, but it would be worth testing them both and seeing what your experience is based on your needs.
You have been a great help Jacob...
Probably where I am struggling is how to structure the code in RoR...
For example, I assume I draw the chart in the main HTML page, then when they filter the data I use a javascript callback to change the data?
The way I had it before was redrawing everything and I know there has to be a more efficient way to do it...
John
Hey John,
You'll have to do some playing around here, but if you take a look at this JSFiddle: http://jsfiddle.net/nanosplit/xv7vut53 I've simplified it down to the bare bones of what's needed to accomplish what you want as if it were hard coded. So now all you have to do is figure out how to make it interactive... this is where ajax comes into play.
Below is pretty much the same thing as the fiddle above, but I've swapped out the hard coded parts to make an ajax call.
foo_controller.rb
def get_updated_data
series_name = "New Data"
series_data = [40000, 50000, 60000, 70000, 80000, 90000, 100000, 120000]
@chart_data = [series_name, series_data]
respond_to do |format|
format.json { render json: @chart_data }
end
end
$(function () {
chart: new Highcharts.Chart({
chart: {
renderTo: 'container'
},
series: [{
name: 'Old Data',
data: [43934, 52503, 57177, 69658, 97031, 119931, 137133, 154175]
}]
});
$('#button').click(function () {
var chart = $('#container').highcharts();
var url = "/get_updated_data"
$.ajax({
type: "GET",
dataType: "json",
url: url,
success: function(data){
newDataName = data[0]
newData = data[1]
var new_series = [{
"name": newDataName,
"data": newData,
}];
for (var i = chart.series.length-1; i>=0; i--) {
chart.series[i].remove();
}
for (var y = new_series.length-1; y >= 0; y--) {
chart.addSeries(new_series[y]);
}
}
});
});
});
For example, I assume I draw the chart in the main HTML page, then when they filter the data I use a javascript callback to change the data?
Yes, basically what you want to do is change the button click function above into a regular function that accepts parameters which you'll then use to construct a new url which the ajax call will use to fetch your new data and then render it
function updateChart(param1, param2) {
var chart = $('#container').highcharts();
var url = "/get_updated_data?param1=" + param1 + "¶m2=" + param2
... rest of the stuff above
So your callback would need to call updateChart()
and pass in the params based on what the user selected - updateChart(foo, bar)
You'll want to make the params part more dynamic since you're probably not going to know ahead of time how many parameters you'll be feeding at once, but this should get you rolling.