About Computed Properties

A lot of people who are new to SproutCore wonder about computed properties at some point.  What is the difference between a computed property and an observer?  This article aims to answer that question for you.  Let’s look at a common example.

Let’s say you are building a contact application, not unlike the sample you can get off of our github site.  Your Contact record would probably have the following properties.

  • firstName - the first name, comes from the server
  • lastName - the last name, comes from the server
  • fullName - the first and last name combined.  Used for display in the contact list, etc.

Obviously, you plan to get the firstName and lastName from your server, but where will the fullName come from?  After all, that is the property you want to display in your contact list.

The Observer Way

It would be best to automatically generate the fullName from your firstName and lastName.  Whenever either one of those properties changes, the fullName property should change also.  Sounds like a job for an observer function.  Your first take on this might look something like:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data
  fullName: '', // default value

  rebuildFullName: function() {
    this.setIfChanged('fullName',
      this.getEach('firstName', 'lastName').compact().join(' '));
  }.observes('firstName', 'lastName')
});

Not bad.  Nice and compact.  Now anytime firstName or lastName changes, fullName will be regenerated.  We even use the nice compact() and join() methods so that if either property is null, the name will still look nice.

But, there is a problem here.  Every time you edit firstName or lastName, you now bear the added expense of recalculating fullName.  Not such a big deal with one contact, but if you load thousands of them or even if you just want to run on Internet Explorer, this added cost really adds up.

Wouldn’t it be better if we could somehow calculate the fullName property on demand instead?  This way you could change firstName and lastName anytime you want.  You only bear the cost of recalculating the fullName property when some part of your application asks for it.  This is where computed properties come in.

The Computed Property Way

Let’s rewrite our class with a computed property now:

Addressbook.Contact = SC.Record.extend({
  // firstName, lastName both added automatically from server or fixture data

  fullName: function() {
    return this.getEach('firstName','lastName').compact().join(' ');
  }.property('firstName','lastName')
});

This code looks really similar to the example we had above, but uses some new notation you may not have seen before.  fullName now points to a function, but that function has a special “property” declaration at the end.  This is how you tell SproutCore that this method is a computed property.  Now, whenever you do something like:

contact.get('fullName');

Instead of simply returning the value of the fullName property, SproutCore will execute the function and return its result.  Hence, a computed property.

The two property names passed to the property() function tell SproutCore that the fullName computed property “depends” on both the firstName and lastName properties.  Whenever either of these properties are changed, SproutCore will also notify observers of the fullName property that it has changed also, just like what happens when you call this.set(’fullName’) in the observer example above.

This computed property approach implements basically the same behavior as we get with the observer method above, but now you only bear the cost of computing fullName when some other part of your app actually needs to use it.

Let’s say you load your contact application with 100 contacts.  Although the firstName and lastName properties will be set on all of the contacts, the fullName property will be calculated only for the 10-15 contacts or so that are visible on the screen.  Instant performance gain.

When to use Computed Properties

Computed properties are a nice way to deal with generating complex values at a lower cost, but when should you use them?

The general guideline is that you should use a computed property when you have one property that is somewhat expensive to generate and that will only be needed some of the time.  Usually you will use computed properties on model objects to compute complex properties, like above, and to lazily load data.

Once you get to the controller or view layer, computed properties have less value because a property you generate here is likely to be used all the time anyway.  Nevertheless, computed properties are another useful tool to add to your arsenal that can give your app a major speed boost, brought to you by SproutCore.

One Response to “About Computed Properties”

  1. Multiple calls to access fullName are going to recompute it every time.

    You could hybridize the approach and set a dirty flag when firstName or lastName changes, then build fullName only when it is requeste, resetting the dirty flag once it is built.


Discussion Area - Leave a Comment