ember emberjs labs

Ember.js and Flot charts

Update (Jun 10th, 2013): Use custom view helpers and view bindings to make it simpler and more reusable.

Continuing to play around with Ember.js I wanted to draw some charts. I’ve used flotcharts with great success in the past but since it’s just a jQuery plugin it is obviously unaware of ember’s amazing data binding capabilities.

Flot requires you to pass in an array of datapoints every time you want to create or update a chart. You can’t use a bound variable in that array so we will have to use observers to manually update flot when our data changes.

Let’s create a flot view that we can re-use in our templates:

App.FlotView = Ember.View.extend({
  classNames: ['canvas'],

  didInsertElement: function() {
    this.renderChart();
  },

  chartUpdated: function() {
    this.renderChart();
  }.observes('datasets'),

  renderChart: function() {
    var canvas = this.$();
    if (canvas) {
      canvas.plot(this.get('datasets'));
    }
  }
});

We’re assuming here that this view is being added on a context where the controller has the property datasets. We observe that property for changes and we ask flot to update the chart accordingly. Here’s how you add that in your template:

Here we pass in our controller’s property datasets. We observe that property for changes and we ask flot to update the chart accordingly. Here’s how you add that in your template:

<script type="text/x-handlebars" id="charts">
  {{view App.FlotView datasetsBinding="datasets"}}
</script>

You can go further and make it cleaner by defining your custom view helper:

Ember.Handlebars.helper('piechart', App.FlotView);

And use it as simple as:

<script type="text/x-handlebars" id="charts">
  {{piechart datasets=datasets}}
</script>

For a simple line chart that datasets property should be something like an array of series [[[1, 3], [2, 14.1], [3.5, 3.14]], [[1, 3], [2, 8], [3.5, 2]]]. Refer to the flot documentation for more details on that.

Let’s say we have a controller like the following:

App.ChartsController = Ember.ArrayController.extend({
  datasets: function() {
    return this.map(function(chart) {
      return chart.get('dataset');
    });
  }.property('@each.dataset')
});

Here we’re creating the datasets computed property that depends on @each.dataset, that is, if the dataset property changes in ANY of the underlying models associated with this ArrayController this property will be updated and the observers (as the one we defined in the view) will be notified.

The dataset property on the model could be returning either one of the mentioned series or an object with more detailed information such as labels, color, etc..

That’s all the non-standard code you need for it to work, everything else is regular models and routes, the result is pretty nice and very responsive. You can also have your models pool for server changes for an auto-updating chart, and it’s definitely a very clean and straightforward implementation for something that would require quite a few events and callbacks if we didn’t have the sweetness of databinding.

I’ve published the complete example using pie charts (their datapoints are simpler) on this JS bin.