Wednesday, October 2, 2013

Integrating AM Charts with Rails using Dynamic Data - An Experience

Recently at work I had an opportunity to take up a task of displaying charts for a statistics module in my project.

These visuals convey more than what plain numbers can, thereby they can instantaneously catch one's attention, even more if they have 3D support.




Why AM Charts ?

These days one of the major challenges for every web application that serves end users is that we need something that has cross browser compatibility. This was one of the top things I had to keep in mind before choosing a chart library.  I needed something that would work even on IE 8. Secondly, I was looking at a library which was freely available to use.

I first gave a shot to d3.js which makes use of SVG for various graphs that it  draws for us. IE 8 didn't have support for SVG and hence I had to look out for other alternatives.

I then tried gRaphael , it had support for IE 6+. The roadblock that I faced with using this chart library was that it had some issues in adding appropriate labels atleast for their barcharts. I even posted a question on stack overflow to see if I could get a workaround, but in vain. Currently there's an open issue with respect to the same on its Github project.

Then on asking one of my friends who I knew had made use of some chart library in the past, I was introduced to AM Charts.

AM charts comes with a link ware license which says that so long as you display the name "www.amcharts.com" alongside the graph you're not bound to pay any money for the usage of their javascript library. It had all that I needed, infact it actually supported more than what I had expected(for e.g., its support for 3D graphs). I made use of AM Charts 2.11.2 in a sample stores application . This is a public project that I've created on Github.

How was the integration of AMCharts with Rails carried out ?

Background - 

My application is a very basic stores application and I was just trying out a sample proof of concept. I wanted to display two things as part of my column/bar graph:-

1. Company Name - String(e.g., say Google)
2. Number of Divisions wrt each company - Integer(e.g., of divisions could be Tablets, Mobiles etc.,)

I was basically making use of divisions_count attribute as a counter_cache in my Company model.  The stores application is a Rails 3.2.11 app using Ruby 1.9.3-p327.

The problem -

The main challenge that I faced while using AM Charts was to ensure that the dynamic data that's given as input to draw the graph was given in the correct format after it had been queried on the server side using Rails.

Now for e.g., when I just gave the below line of code in my company_division_stats2.html.erb view file and hit http://localhost:4000/companies/company_division_stats2 on my browser

var chartData = <%= @all_companies %>

The screen was simply blank !! There wasn't any graph displayed. I opened up the rails console to inspect the data that Rails was actually fetching for me from the database wrt the below lines of code using the company_division_stats action as part of the Companies Controller:-

@all_companies = @companies_data = Company.all

@specific_details = @all_companies = @all_companies.map {|each_company| { country: each_company.name, visits: each_company.divisions_count} }

@all_companies had data in the format:-

[{:country=>"Samsung", :visits=>8}, {:country=>"Natraj", :visits=>2}, {:country=>"Tupperware", :visits=>5}, {:country=>"Transcen", :visits=>0}, {:country=>"camlin", :visits=>0}]

If you observe closely, the sample input that the AM charts actually takes is of the below format:-

var chartData = [{
          country: "USA",
          visits: 4025
      }, {
          country: "China",
          visits: 1882
      },...];
Before you start working on the customized project requirements that you may need using AM Charts, you might very well want to first navigate your way through various samples that AM Charts provides you as part of their chart library. The same is available for download through this link.

The solution -


After using my good old friend google :), in search for a solution, I came across a similar question on stackoverflow and tried the approach mentioned as part of the accepted answer for this question, but even this didn't work for me. The problem(s) that I faced using the solution mentioned in the accepted answer is posted as a separate question on stackoverflow in case you would like to see those details.

What finally worked for me was a blog link that was posted as a comment to the accepted answer. I'm grateful to diasks2 for his solution to this problem that I had faced.  Finally Relieved :), I was just thinking about what and all one has to try inorder to get to the right solution :). But I'm happy I had finally found a workaround that did the trick for me. The blog has a detailed tutorial on how to integrate AM Charts from scratch with Rails. The objective of this blog is to directly pin point the problem and suggest solutions around it to some detail.

I did look at the solution mentioned in the blog link but I still wasn't clear what I was doing wrong from my end. That's basically why I fired up the Rails console to understand what's actually happening behind the scenes.

Now if you look back at the original problem, I had to convert the symbols in @all_companies which was an array of hashes to non symbols, such that it is in a format which is understood by AM Charts. That was made possible using the to_json method. Here's how @all_companies looks on the application of the to_json method:-

@all_companies.to_json
 => "[{\"country\":\"Samsung\",\"visits\":8},{\"country\":\"Natraj\",\"visits\":2},{\"country\":\"Tupperware\",\"visits\":5},{\"country\":\"Transcen\",\"visits\":0},{\"country\":\"camlin\",\"visits\":0}]" 

I no more had to deal with symbols now and I was a step closer to using valid JSON which is required as input data for the AM Charts.

Lastly, if you want to ensure that your html content stays unescaped, you need to use the html_safe method. You can read more on why exactly we use the html_safe method from this link. So finally the one line of code that was like the key to opening a treasure buried in some distant location was:-

var chartData = <%= @all_companies.to_json.html_safe %>
This works well. But best practice adhering to the Rails principle of keeping the code DRY would be to place this part of the logic in a helper method. I've done that in my application wrt another view and action(company_division_stats). I've just shown both the ways so that one can clearly understand what's the difference and what actually is the best way of doing it going forward.