Ember Computed Properties in Practice

For me, our class' transition from Rails to Ember.js was an exciting one. I felt as though it marked me taking one step closer to being a real developer - for the first time, harnessing the ultimate developer super-power of being able to transfer programmatic knowledge and thinking across languages and frameworks. This enthusiasm was bolstered after brief exploration - while the land looked quite different than that of ruby and rails…it felt familiar in an exhilarating way. This optimism, coupled with the fact that the Ember mascot is an adorable hamster…I mean, come on.

In this post, I want to highlight one feature of Ember I think is really cool, computed properties, and show some use cases and code from a recent project I worked on.

What are computed values in Ember? Let’s look at the docs:

In a nutshell, computed properties let you declare functions as properties. You create one by defining a computed property as a function, which Ember will automatically call when you ask for the property. You can then use it the same way you would any normal, static property.

The code found in the docs highlight pretty simple executions, defining a property of an object that combines other properties in some way (e.g. combining a first name and a last name). Coming from ruby, this initially didn’t seem that special - the easiest analog would be some sort of helper method defined in a class or view helper. However, it wasn’t until pretty far along in our Ember project, that the real value became clear to me and I was able to write some cool code. More on that in a bit.

Here’s another, slightly more complex example of computed property usage. In this case, we want to be able to easily produce a list of the conferences that our Programmer object is the keynote speaker of:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// programmer.js
export default Ember.Object.extend({
  firstName: null,
  lastName: null,
  conferences: [ ],

  // simple example found in the Ember docs
  fullName: Ember.computed("firstName", "lastName", function( ){
      return `${this.get('firstName')} ${this.get('lastName')}`;
    }),

  // more complex example, utilizing the properties of a collection of conference objects
  keyNoteConferences: Ember.computed("conferences.@each.keyNote", function( ){
    let keyTalks = [ ];
    this.get('conferences').forEach(function(conf){
      if (conf.keyNote === this.get('fullName')) {
        keyTalks.pushObject(conf);
      }
    }.bind(this));
    return keyTalks;
  })
})

In our Ember project, Globo, we needed the ability to dynamically access the ‘pins’ of a user’s trip. Due to the associations of our models in Ember and the Rails API, this meant that we had to go through ‘destinations,’ a join table of the ‘trips’ and ‘pins’ of the current user. In a rails-only application, this would have been a walk in the park - a quick helper method in the ‘trip’ class. However, as we saw above, it’s not that simple in Ember - because of the file structure (separation of models, routes, controllers, etc), the concept of ‘this’ (or ‘self’ in rails) is pretty dynamic depending on where you’re calling it from. See below for a screenshot of the outcome we were looking for, a poly-line (representing a ‘trip’) connecting multiple ‘pins’:

In order to represent this connection, we needed to create a computed property for a trip, ‘pinPoints,’ which would return an array of coordinates that our Leaflet map component could use for the poly-line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// app/models/trip.js
export default DS.Model.extend({
name: DS.attr('string'),
web_color: DS.attr('string'),
startDate: DS.attr('date'),
user: DS.belongsTo('user'),
createdAt: DS.attr('date'),
destinations: DS.hasMany('destination'),

pinPoints: Ember.computed('destinations', function( ){
  let locationArray = [ ];
  this.get('destinations').forEach(function(destination){
    let pin = destination.get('pin');
    let coords = L.latLng(pin.get('lat'), pin.get('long'));
    locationArray.push(coords);
  });
  return locationArray;
}),

With this property established, we could then access it on the front end via handlebars to establish the poly-lines:

Resources: