Creating A Metrics Dashboard With Ember.js, Bootstrap, and Rails - Part 3

This is part three of a series on building a metrics dashboard with Ember, Bootstrap, and Rails. Over the next few weeks I will be building out more functionality and writing posts to cover that. If you haven’t read part one and part two then that’s a good place to start.

In part two we ended up with an Ember app that rendered dynamic tables. If you followed along, your page should now look like this:

Part Two Final

Today we’re going to add some graphs and see how Ember components work.

Remember, if you get stuck you can find all of the code for this post at github.com/jtescher/example-ember-rails-dashboard.

Choosing The Right Library

Highcharts Demo

There are many good options when it comes to JavaScript graphing, charting, and visualizations. I find highcharts to be a good place to get started and it is free for non-commercial uses! If you find yourself needing more control or having a very specific requirement you can always look at projects like d3.js.

Adding Highcharts

Let’s download the latest version of highcharts to our vendor/assets/javascripts directory.

$ curl http://code.highcharts.com/4.0.1/highcharts.js \
    -o vendor/assets/javascripts/highcharts-4.0.1.js

And then require the file in app/assets/javascripts/application.js

...
//= require jquery
//= require accounting-0.3.2
//= require highcharts-4.0.1
//= require_tree .

Creating The Ember Component

Ember makes adding reusable components quite simple. We can add a component that represents a specific chart we want to show on the screen and have ember re-render the chart whenever the data changes. You can read more about how the Ember components work here.

As an example we can add a highcharts column chart to show revenue by product. First let’s add the component in our app/assets/javascripts/templates/orders.hbs file:

<h1>Orders</h1>



<table class='table table-striped'>
...

Then we can add the template for the component in app/assets/javascripts/templates/components/column-chart.hbs:

<div  style='width: 100%;'></div>

And finally we can define the component in app/assets/javascripts/components/column-chart.js.coffee:

Dashboard.ColumnChartComponent = Ember.Component.extend
  tagName: 'div'
  classNames: ['highcharts']

  contentChanged: (->
    @rerender()
  ).observes('series')

  didInsertElement: ->
    $("##{@chartId}").highcharts({
      chart: { type: 'column' },
      title: { text: 'Revenue by Product' },
      legend: { enabled: false },
      xAxis: {
        title: {
          text: 'Product Number'
        }
      },
      series: [{
        name: 'Quantity',
        data: [4, 4]
      }, {
        name: 'Revenue',
        data: [10.0, 10.0]
      }]
    })

  willDestroyElement: ->
    $("##{@chartId}").highcharts().destroy()

Then when you reload the page it should look like this:

Orders Static Column Chart

Binding Data To The Ember Component

The chart we have is currently always showing the same series because we hard coded it in the component. Let’s now make this dynamic by adding the data in the route and using data bindings.

First let’s update the data in our orders route to include a product id.

# app/assets/javascripts/routes/orders_route.js.coffee
Dashboard.OrdersRoute = Ember.Route.extend({
  model: ->
    [
      {
        id: 1,
        firstName: 'James',
        lastName: 'Deen',
        quantity: 1,
        revenue: '10.00',
        productId: 0,
      },
      {
        id: 2,
        firstName: 'Alex',
        lastName: 'Baldwin',
        quantity: 2,
        revenue: '20.00',
        productId: 1,
      }
    ]
})

And then we can build our chart series in the orders controller (this is a very simplistic example):

Dashboard.OrdersController = Ember.ArrayController.extend({

  ...

  chartSeries: (->
    revenues = @map((order)->
      parseFloat(order.revenue)
    )
    quantities = @mapBy('quantity')

    [
      {
        name: 'Quantity',
        data: quantities
      },
      {
        name: 'Revenue',
        data: revenues
      }
    ]
  ).property('@each')

})


We can then bind chartSeries in orders.hbs:

<h1>Orders</h1>



<table class='table table-striped'>

And finally use series in our chart component:

# app/assets/javascripts/components/column-chart.js.coffee
...
didInsertElement: ->
  $("##{@chartId}").highcharts({
    chart: { type: 'column' },
    title: { text: 'Revenue by Product' },
    legend: { enabled: false },
    xAxis: {
      title: {
        text: 'Product Number'
      }
    },
    series: @series
  })
...

We then end up with our final dynamic chart rendered by Ember: Orders Static Column Chart