Basic Implementations

Computed properties are very handy properties that we can declare as functions. These properties will run when the property is called or its dependent keys change. When creating a computed property dependent keys are declared. There is no limit to these dependencies (but be reasonable). When our arguments/dependencies are updated our computed property will update as well.

We have a few simple properties set in our controller.

   export default Ember.Controller.extend({
     firstName: 'Eddard',
     lastName: 'Stark',
     castle: 'Winterfell',
     kingdom: 'North',
     title: 'Lord',
 

A computed property’s basic structure is as follows, Ember.computed, passing in our Ember keys (what we would like the computed property to listen to changes in), and then a function that returns our current property value.

   fullName: Ember.computed('firstName', 'lastName', function(){
   return `${this.get('firstName')} ${this.get('lastName')}`
  }),
  // Returns Eddard Stark

  land: Ember.computed('castle', 'kingdom', function(){
   return `The ${this.get('kingdom')} at castle ${this.get('castle')}`
  }),
  // Returns The North at castle Winterfell

Computed properties are used like any other static Ember property. They are called the same in hbs files and can also be set as dependencies on other computed properties. When this occurs it is known as chaining.

  fullTitle: Ember.computed('fullName', 'land', 'title', function(){
    return `${this.get('title')} ${this.get('fullName')} warden of ${this.get('land')}`
  }),
  // Returns Lord Eddard Stark warden of the North at castle Winterfell

For example, if firstName is changed fullName is computed which will cause fullTitle to compute. Computed properties have better performance over functions as they are only computed if the dependencies change. Lets say land changes and fullName does not, only land will be computed prior to fullTitle’s computation. Whereas a function would compute all of these properties every time.

Lists and Objects

Computed properties are also very effective when working with lists. We can tell our computed property to watch for changes in specific list columns.

  characters: [
    { name: 'The Hound', kills: 500, saves: 2, alive: true },
    { name: 'Joffrey Baratheon', kills: 1, saves: 0 , alive: false},
    { name: 'Hodor', kills: 0, saves: 2, alive: false },
    { name: 'Arya Stark', kills: 15, saves: 1, alive: true },
    { name: 'Bran Stark', kills: 0, saves: 0, alive: true },
  ],

Say we want to figure out how many people have been killed from our sample of Game of Thrones characters. We can take our list of characters, and listen for changes in the kills column of our list. As a character’s kills increase our totalKills computed property will increase, keeping us up to date on the latest carnage.

  totalKills: Ember.computed('characters.@each.kills', function({
    var total = 0
    this.get('characters').forEach(function(character){
      total += Number(character.kills)
      })
      return total
    });
  // returns 518

Let’s break down the property key characters.@each.kills. characters tells us we are watching the characters array, if an addition or deletion occurs we will need to run a computation. Next .@each tells us to watch each of the individual objects. .kills tells us to be even more specific and only watch for changes to the kills property of each object. Here we are listening only for characters.@each.kills. However, as with our computed property of fullName, totalKills can take multiple dependencies. Let’s say that lives saved negates lives taken. In this case we can improve our computed property further by interacting with the saved column as well as the kills column.

  totalKills: Ember.computed('characters.@each.{kills,saves}', function(){
    var total = 0
    this.get('characters').forEach(function(character){
      total += Number(character.kills)
      total -= Number(character.saves)
      })
      return total
  }),
  // returns 515

While calling each key separately as

  ('characters.@each.kills', 'characters.@each.saves')

is valid there is an easier syntax

  'characters.@each.{kills,saves}'

Any number of these may be added, however, the syntax is only valid when there are no spaces between properties. It should be noted that multiple .@each calls in a single key are invalid. .@each only works one level deep. This is due to the fact that multiple each calls would cause excessive computations.

James Cameron sure does love knocking off my favorite characters. Let’s use filterBy to return all of our living characters objects.

  alive: Ember.computed('characters.@each.alive', function(){
    var chars = this.get('characters');
    return chars.filterBy('alive', true);
  }),
  // returns 3 character objects where alive is true

Finally after all this talk of killing lets take a second to enjoy some pretty code. Ember computed properties have a number of macros available including filterBy. Macros are shorter ways to express common types of computed properties. The below code has the exact functionality of our above code with a much cleaner look.

  notAlive: Ember.computed.filterBy('characters', 'alive', false),

I hope you enjoyed!