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>

{{column-chart chartId='revenue-by-product'}}

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

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

<div {{bind-attr id='chartId'}} 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>

{{column-chart chartId='revenue-by-product' series=chartSeries}}

<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

 
74
Kudos
 
74
Kudos

Now read this

Building A Go Web App with Revel on Heroku

I’ve been interested in Go for a long time now. The language has lots of aspects that make it well suited for building modern web applications including its powerful standard library, concurrency primitives, and impressive performance... Continue →