#223 Charts & Graphs (revised)
Sep 15, 2012 | 12 minutes | Performance, Views
If you have a lot of data, consider adding a graph to provide an overview of it. Here I show how to use Morris.js to chart an Order model and visualize trends in the data.



What would you recommend for charts in both html and a generated pdf? Ever tried one of these libs with v8 on the server?
Usually, I stick with LaTeX and PGF/TikZ to produce PDF graphs.
This has some disadvantages, though:
It's more of an advanced task (not the Ruby/Rails part, but the LaTeX one), but if you value the high quality of TeX'ed documents, it's definitely the way you should go.
Using v8 on the other hand sounds far to nice to be true :-)
Great episode another option is: Google Charts
Is Google charts an option for proprietary data? I have an app that is strictly for internal use.
Any thoughts about jqPlot?
I use jqPlot in a project at work and so far I like it. It does have somethings which may be hard to implement. However you can use the gem I wrote plot simple(documentation) to get around this.
Morris.js looks super simple to setup and use.
My line charts required logarithmic Y-axis. So I had to settle on the Google Visualization API with the Visualr Gem. However, over time, I found it somewhat rigid. Therefore, I had to go to HighCharts which can do just about everything that I need to do. As Ryan points out, it is a commercial product though.
index_by works a lot better after a group by query compard to group_by.
Agreed - There's no need to use 'first' if you use index_by. The hash values are instances as expected.
Another option - use each_with_object
orders.each_with_object({}) { |o,hsh| hsh[o.purchased_at.to_date] = o.total_price }it's even better
unless I'm missing something important ^^
Found nvd3 which seems pretty nice.
Quite interesting should also be Dimensional Charting Javascript Library.
Should
Time.zone.nowbe in a lambda?You can also use g Raphael which is the library Raphael provides for drawing charts
first of all, thanks... that's just what i've needed now.
But does anybody know why the video content is different from the Show Notes or even the Source Code?
Edit: Sry, didn't see the Notes.
I use amCharts. It has some pretty advanced charting features for those needing them. It is a commercial product but well maintained and totally worth it. Available in Flash and Javascript versions.
Google chart is also a good tool to display live data.
Considering that Morris is Javascript (and thus doesn't generate graphs server-side), it seems disappointingly complicated to use in the sort of situations demonstrated in this episode.
Specifically, since the data to be graphed is already present in the markup, it would be nice if Morris could be instructed to create the chart directly from that tabular data as represented by the markup itself. This would reduce duplication by not having specific Rails code for generating Morris-specific data, as well as removing clutter from the rendered markup which can make it more cumbersome to do certain debugging tasks, and which is only useful to Javascript anyway (and not, e.g. screen readers).
The above could imply a situation in which all of the data is present in the markup--as opposed to say, the latest data only, with the chart perhaps a representation of a longer time-frame--but as it is, Morris still would need all of that data to be present in the markup anyway (albeit in a data attribute). This could be solved nicely via AJAX, and perhaps fetching sampled data (if the data set is large), but such a request could still respond with a document fragment containing tabular markup, and thus still removing any need for separate chart-rendering code (but possibly adding a small amount of complexity for specifying things like sample resolution). This would also be a reasonable approach when only the chart itself is meant to be shown.
It's (sadly) out of date, but there was a good Javascript library that could in fact take data directly from the markup, and used the canvas to render its output: Bluff, isn't visually polished enough by today's standards, but is a good example of such functionality in action, and really and truly is easy to use.
I'm getting 'Graph Placeholder not found' despite the div id being on the page. Any ideas?
See Sanjeev's and my replies below
I am getting the 'Graph Placeholder not found' aswell, which breaks most of the JS on my site, can we get a solution on this please?
See Sanjeev's and my replies below
Hi i can't make the charts appearing using twitter bootstrap, is there any aditional task to do to?
Thanks
Hi,
i got the chart working under twitter bootstrap, I had to use a .js file rather than a coffee-script file for the code that created the graph.
I'm having issues with it too, running under bootstrap I get an error in the javascript console: "undefined is not an object (evaluating _ref.length)"
I'm using it with the test data, not even a dynamic dataset.
I just figured this out under the javascript debugger, for some reason the coffee script version does not set the xkeys or xkeys options, using plain old javascript works even under bootstrap.
Maybe try this: http://iqwen.net/question/58396
Anybody having trouble getting the data attribute working on rails 3.0.1?
I have added morris and raphael inside vendor/javascripts and did call in application.js also. In my orders.js.coffee i put
jQuery ->
Morris.Line
element: 'annual'
data: [
{y: '2012', a: 100}
{y: '2011', a: 75}
{y: '2010', a: 50}
{y: '2009', a: 75}
{y: '2008', a: 50}
{y: '2007', a: 75}
{y: '2006', a: 100}
]
xkey: 'y'
ykeys: ['a']
labels: ['Series A']
for testing purpose and did add in index. But chart did not appears.
nevermind, I figured it out, the code should have been
jQuery ->
Morris.Line({
element: 'annual'
data: [
{y: '2012', a: 100}
{y: '2011', a: 75}
{y: '2010', a: 50}
{y: '2009', a: 75}
{y: '2008', a: 50}
{y: '2007', a: 75}
{y: '2006', a: 100}
]
xkey: 'y'
ykeys: ['a']
labels: ['Series A']}
)
Thanks Sanjeev,,,for those who don't notice the subtle difference... Morris.Line({....}) needs to separate each different chart/graph call with different tags...otherwise it will only see the first and you'll get the "'Graph Placeholder not found'" or "Element not found" errors.
hey,
i released a gem for morris.js so you don't need to add the assets manually.
You can find it over at Github (https://github.com/beanieboi/morrisjs-rails)
very handy, thank you :)
So dumping orders array into data-attribute exposes all the attributes stored in db to Morris. What would be the best way to inject virtual attributes to the objects in array handed over to javascript side?
Thanks Ryan :-)
Hi guys,
I am new to rails. How do I make this example show the data by hour?
My database has two relevant columns: "data_time" and "values". I have a few values per hour, so, I wanted to plot them all. I was able to group by hour changing:
group("date(data_time)")
to:
group("strftime('%Y-%m-%d %H', data_time)")
But the graph still shows only per day. I suspect it has something to do with:
(start.to_date..Date.today).map do |date|
but I can't figure out what it is!
A bit late, but I hope this helps explain how to iterate over an arbitrary time period: http://stackoverflow.com/questions/501253/iterate-over-ruby-time-object-with-delta
I adapted Ryan's example to the code below, which works just fine on SQLite. However, on PostgreSQL (Heroku), I get the error
PGError: ERROR: column "users.created_at" must appear in the GROUP BY clause or be used in an aggregate functionAny ideas on making this work in PostgreSQL?
Here's the relevant code:
I fixed this by changing
created_atin theselectclause todate(created_at). In my case, I also needed to addunscoped.before thewhereclause in order to eliminate my model's default ordering byname, which PG also complained about for the same reason, and I added aorder('date(created_at)')for good measure.Here's the edited code that works in PostgreSQL:
Thank you John for the snippet! It was really helpful for me.
I follow everything exactly, but when I try to display the graph with the fake information, nothing displays. What could possibly be going wrong?
I am using Morris chart. How do i reverse the values on the Y axis.
I am tracking ranks hence lower the better.
http://www.siterankhistory.com
I can't get the demo started. The console gives me this error Uncaught TypeError: Cannot call method 'match' of undefined.
I have created a stackoverflow thread as well to see if anyone else stumbled across this.
http://stackoverflow.com/questions/16267904/issue-getting-started-with-morris
Issue solved
I'm rather new to rails and javascript/coffee
could some1 please explain to me the logic behind this line:
data: $('statistics_chart').data('statistics')
what is the magic so to speak, behind the scenes?
you are populating the statistics_chart div with data from data-statistics attribute of that div, look in inspect element and you'll see the html5 attribute called data-statistics with an array of hashes inside.
First sign in through GitHub to post a comment.