Answer: how to pull array modeled data with Ember Data and c3.js

Wednesday, January 21st 2015

In my previous blog post I was asking how to properly pull array modeled data with Ember Data for C3.js. Thanks to Mark Oleson I have it figured out!

The biggest problem I had was taking the data from the Analytics API and forming it so Ember Data would take it. So here's what the rails api controller code looks like:

def index
  params[:startDate] ||= "2015-01-01"
  params[:endDate] ||= "2015-01-16"

  analytic = {
    analytic: [
      id: "current",
      date: ["date"],
      pageview: ["Pageview"]
    ]
  }

  google_analytics = GoogleAnalytics.new
  data = google_analytics.visitors(params[:startDate], params[:endDate])

  data.each do |key, value|
    analytic[:analytic][0][:date] << key
    analytic[:analytic][0][:pageview] << value
  end

  render json: analytic
end

Let me try to bullet point whats going on here:

  • First if the params from startDate and endDate are nil, I set them to a default date just to be safe.
  • We create a hash named analytic which has a root element array named analytic.
  • Inside the analytic array we hard coded the id to "current". Just because Ember Data requires an ID.
  • We create two arrays with the strings date and Pageview. Date is used to help c3.js know what the x axis is and pageview is my legend label.
  • I create a new GoogleAnalytics and then call the visitors method to pull my data
  • Loop through the data taking the key and value to map into their respective arrays. (Key = date, value = pageview)
  • Render it out as JSON and this is what you get:
{
  "analytic": [
    {
      "id":"current",
      "date":["date","2015-01-01","2015-01-02"],
      "pageview":["Pageview",10,11]
    }
  ]
}

After I got the data coming from the API looking right, I moved on to setting up my model in Ember.

import DS from 'ember-data';

export default DS.Model.extend({
  date: DS.attr(),
  pageview: DS.attr()
});

It's important to leave DS.attr() blank! Next in my controller I pull the data by doing:

setDateAndData: function(startDate, endDate) {
  startDate = this.get("graphStartDate") || startDate;
  endDate = this.get("graphEndDate") || endDate;
  var self = this;

  this.set("loadingData", true);

  return this.store.find('analytic', { startDate: startDate, endDate: endDate }).then(function(data) {
    var graphData = data.content[0]._data;
    self.set("loadingData", false);

    return self.set("controllerData", {
      x: "date",
      columns: [
        graphData.date,
        graphData.pageview
      ],
      type: "bar"
    });
  });

}.on("init")

I'm totally open to better ways to do this, but this works!

Let me know if you have any questions, I'm more than willing to help! You can comment here, reach out on github, or twitter.