Ember.js is like a swiss army knife. It comes with a lot of built in fancy tools designed to make your day to day life just that much easier. The tools built in to Ember allow us to add functionality to a website that could potentially take hours to build in a fraction of the time. Knowing the tools in this swiss army knife that is Ember, as well as knowing when to use each one, makes all the difference. In this post, I’m going to talk about one of larger and more versatile tools in the Ember arsenal: computed properties.

What are computed properties?

Computed properties are simply functions as properties. Logic to return a value . For example, firstName and lastName are separate properties, but are commonly referenced together to represent a full name. In Ember we can make a computed property that will return the full name with the most current value of firstName and lastName tied together:

fullName: Ember.computed('firstName', 'lastName', function() {  
    return this.get('firstName') + ' ' + this.get('lastName');
})

If firstName were set to Scott and lastName set to Smith, fullName would give you “Scott Smith” according to the code above. In this example, the first two parameters are firstName and lastName. This means Ember will watch those properties and any time their value changes, the value of fullName will be immediately recalculated and updated. That’s the beauty of binding in Ember.

Working With Arrays

You can also use computed properties with arrays. Here are two examples:

var List = Ember.Object.extend({  
  completedTasks: Ember.computed('tasks.@each.isCompleted', function() {
    return this.get('tasks').filterBy('isCompleted', true).get('length');
  }),
  totalTasks: Ember.computed('tasks.@each', function() {
    return this.get('tasks').get('length');
  })
});

var list = List.create({  
  tasks: [
    { isCompleted: false },
    { isCompleted: true },
    { isCompleted: false }
  ],
});

list.get('completedTasks'); // 1  
list.get('totalTasks'); // 3  
list.set('tasks.firstObject.isCompleted', true);  
list.get('completedTasks'); // 2  

In this example, we have an array called tasks that contains three objects that each have a property of isCompleted.

Our first computed property, completedTasks, filters the array by every object with an isCompleted property of true and returns the count of how many records remain. Notice the first parameter, tasks.@each.isCompleted. This tells Ember that whenever the value of isCompleted changes in any of the objects of the tasks array, the value of completedTasks should be recalculated.

The second computed property, totalTasks, returns the total number of objects in the tasks array. The first parameter tasks.@each tells Ember to recalculate the value of totalTasks any time a new object is added to/remove from the array. Without specifying a property after .@each, Ember will watch the structure of the array itself, not properties of the objects within it. If an object were removed from tasks, the value of totalTasks would update to 2.

Chaining

You can also chain computed properties together. For example:

var Person = Ember.Object.extend({  
  fullName: Ember.computed('firstName', 'lastName', function() {
    return this.get('firstName') + ' ' + this.get('lastName');
  }),
  nameAndAge: Ember.computed('fullName', 'age', function() {
    if (this.get('fullName') && this.get('age')) {
        return this.get('fullName') + ', ' + this.get('age') + ' years old';
    }
  })
});

var scott = Person.create({  
  firstName: 'Scott',
  lastName: 'Smith'
});

scott.get('fullName'); // "Scott Smith"  
scott.set('age', 33);  
scott.get('nameAndAge'); // "Scott Smith, 33 years old"  

In this example we have three properties: firstName, lastName, and age. Next we have a computed property of fullName that simply concatenates firstName and lastName. After that, we have another computed property called nameAndAge. This ties fullName and age together. Notice that nameAndAge is watching the computed property of fullName. If firstName were to be updated to “Mike”, the change would reflect down the chain: fullName will recalculate to “Mike Smith” and nameAndAge will recalculate to “Mike Smith, 33 years old”. Pretty nifty stuff.

Macros

There are many computed property macros built directly into Ember to make your life just a little easier. Here are some of my favorites:

Ember.computed.and

Does a logical 'and' on all provided properties

var Swimmer = Ember.Object.extend({  
  readyToSwim: Ember.computed.and('hasSwimsuit', 'hasTowel')
});
var swimmer = Swimmer.create({  
  hasSwimsuit: true,
  hasTowel: false
});

swimmer.get('readyToSwim'); // false  
swimmer.set('hasTowel', true);  
swimmer.get('readyToSwim'); // true  

Ember.computed.equal

Returns true when the supplied property (tasksCompleted) equals the supplied value (totalTasks)

var Chores = Ember.Object.extend({  
  allDone: Ember.computed.equal('tasksCompleted', 'totalTasks')
});
var chores = Chores.create({  
  totalTasks: 4,
  tasksCompleted: 2
});

chores.get('allDone'); // false  
chores.set('totalCompleted', 4);  
chores.get('allDone'); // true  

Ember.computed.gt

Returns true when the supplied property is greater than the supplied value

Ember.computed.lt

Returns true when the supplied property is less than the supplied value

And many more!

There are lots of macros to choose from and you can even make your own.

This is only the tip of the iceberg for computed properties. They are very powerful tools that we use everywhere in our code. Harnessing their power will open up doors for fancy functionality while simplifying your codebase. Try them out today!