Skip to content

Instantly share code, notes, and snippets.

@stevekinney
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevekinney/11113617 to your computer and use it in GitHub Desktop.
Save stevekinney/11113617 to your computer and use it in GitHub Desktop.

The cool thing about Ember is you can create Web Components that encapsulate reusable functionality. A component has two parts, a template (usually in Handlebars) and some JavaScript.

One of the nice things about Ember is that most of the names are just assumed. So if you pop a new component called {{my-chart}} into your application, Ember automatically looks for a template for that component in #{wherever_you_keep_your_templates}/components/my-chart.hbs and tries to create an instance of App.MyChartComponent. You can use App.MyChartComponent.extend({}) anywhere in your application to defite it's functionality.

The first step is pretty straight forward. With an Ember Component, you can grab onto the didInsertElement hook and initialize the graph. Here is a code sample that basically shows how I did that.

  didInsertElement: function () {
    
    var width = this.$().parent().get(0).offsetWidth;
    var height = this.get('height');
    var horizontalMargin = this.get('horizontalMargin');
    var verticalMargin = this.get('verticalMargin');
    
    var id = this.$().attr('id');
    
    var dataset = this.get('lorenzifiedData');

    var svg = d3.select('#'+id)
      .append('svg')
      .attr({
        width: width,
        height: height
      });

    // A whole lot of chart configuration has been truncated for brevity.
    // See the link above if you want to see the D3 code that generates
    // the chart.
      
    this.set('svg', svg);
    this.set('path', path);
    this.set('line', line);
  },

Ember has data-binding like Angular. When the data changes, we want the graph to update. In order to get this part working, we create a method on App.MyChartComponent. I called it updateChart for obvious reasons, but you can call it whatever you want. The secret sauce is at the end of the function call, you add .observes('data'). Again, data is arbitrary, it should be whatever the name of the property is that stores your data. In my code, I'm actually observing a property called lorenzifiedData which, in turn, is computed from a property called data. It's just basically doing some massaging of the data before passing it along to my chart.

  updateChart: function () {
    
    var id = this.$().attr('id');
    var data = this.get('lorenzifiedData');
    
    // If the data doesn't is free of NaNs, update the graph.
    if (!_.some(data, _.isNaN)) {
      this.get('path')
        .datum(data)
        .transition()
          .duration(750)
          .ease("linear")
        .attr('d', this.get('line'));
    } else {
      this.get('path')
        .datum([0,0,0,0,0,0])
        .transition()
          .duration(750)
          .ease("linear")
        .attr('d', this.get('line'));
    }
    
  }.observes('lorenzifiedData'),

Now, whenever the data changes, the chart updates. In the example above, I actually use this chart once, but there is nothing stopping me from including it in multiple places in my app. You can pass in attributes like {{my-chart title="Hamburgers Eaten" width="600" data=data}} to define your own data. By default, components don't have access to anything you don't pass into them. You can use a view if you want it to be aware of your controller, but components seem to be the recommended best practice these days. In a pinch, you can just pass the entire model into the component.

Some additional resources:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment