I'm generally of the opinion that if it ain't broke don't fix it, but my current solution to this issue (though it does work) seems really ugly. I am using Ruby, Sinatra, Haml, and Highcharts, and I'd like to be able to pass data grabbed in Ruby and send to Highcharts.
Currently, I'm essentially just creating a string object out of the entire Highcharts javascript and interpolating the ruby data into that. This works, but should I be taking a completely different approach to this? Here's exactly what I'm doing:
I've defined a HighChart class with several properties that I want to be able to change via Ruby (e.g. chart title, data, etc). The example below (which works!) only sets two of these properties, leaving the rest as default:
get '/' do
@chart = HighChart.new(options={
:title_text => "Test Title",
:series_data =>
{
'Calvin' => [10, 2, 17],
'Hobbes' => [11, 14, 6]
}
}
)
haml :index
end
I'm using Haml's javascript filter in the view:
:javascript
#{@chart.chart_js}
So far so good. The ugly part is the chart_js method. I have simply copied the full text of the HighChart javascript, pasted it in as a string, and then used Ruby interpolation to grab the variables from the HighChart object created in the controller:
def chart_js
chart_js = "
var chart1;
$(document).ready(function() {
chart1 = new Highcharts.Chart({
chart: {
renderTo: '#{render_to}',
type: '#{chart_type}'
},
title: {
te开发者_高级运维xt: '#{title_text}'
},
xAxis: {
categories: #{x_categories}
},
yAxis: {
title: {
text: '#{y_categories}'
}
},
series: [#{get_data_js(series_data)}]
});
});
"
end
And just in case it's helpful, here's the first part of the HighChart object class definition:
class HighChart
attr_accessor :render_to, :chart_type, :title_text, :x_categories,
:y_categories, :series_data
def initialize(options={})
self.render_to = options[:render_to] || 'container'
self.chart_type = options[:chart_type] || 'bar'
self.title_text = options[:title_text] || 'Fruit Consumption'
self.x_categories = options[:x_categories] || ['Apples', 'Bananas', 'Oranges']
self.y_categories = options[:y_categories] || 'Fruit eaten'
self.series_data = options[:series_data] || {'Jane' => [1, 0, 4],
'Bobbert' => [5, 7, 3],
'Zach' => [10, 2, 1]
}
end
You could make a view specifically for the Highcharts JS, and then use instance variables (eg. @render_to, @chart_type, etc.). That won't really clean up the code that much, but it will get rid of some of the "wrongness" of having a giant view string in your controller.
Fundamentally though there's not much else you can do; you have a bunch of server-side info, and it has to be written to the client (in the specific Highcharts format). There's just no getting around that.
The only other suggestion I can offer is to use intermediate JS variables. This would let you keep all of the JS in the view (without a JS-only view), and then you would just need to set some JS variables. This would look something like this:
var RENDER_TO = "@render_to";
var CHART_TYPE = "@chart_type";
// ...
$(document).ready(function() {
chart1 = new Highcharts.Chart({
chart: {
renderTo: RENDER_TO,
....
Now this actually involves more code, but it would let you totally separate your onReady code from the server-value-writing code.
Hope that helps.
精彩评论