Ember.js - Taming computed properties
In Ember.js there is this feature called computed properties. You can learn more about what computed properties are in the official Ember.js guides. Instead I wanted to talk about how they act depending on what you pass to
Anyway, I was watching and taking notes from “Introduction to Ember.js” presentation by Luke Melia which he gave a couple of months ago at NYC Ember meetup. At some point Luke was explaining computed properties and what’s the difference between
content is an array consisting of objects and these objects have
isDone property which is set either to
This is where I decided to basically take what Luke said and turn it into a blog post.
To better illustrate how these computed properties act depending on what gets passed to
.property() I wrote yet another TODO application example. A task list with ability to add items and mark them as completed is a perfect example. Here’s a complete JSBin for you to play around with and see the end result. Don’t get carried away just yet because I still want to walk you through some of the code and explain what is what.
As you can see in the Output window we already have two todos. You can mark each of those todos as (in)complete by clicking a checkbox right next to them. You can add new todo items by clicking Add button. You can’t delete a todo, though but we can live without it in this example. There are also three counters each displaying a result depending on what I have passed to
Give me the codes
I started by creating two objects with the same properties. The only difference is that
isDone property for the
secondTodo is set to
1 2 3 4 5 6 7 8
I used these objects in the
model hook to wire them up with index template.
1 2 3 4 5
Next, I wrote
IndexController. It inherits from
Ember.ArrayController because I’m representing a collection of models (
secondTodo) and this way
content property will be automatically set up to have these two objects. YAY for Ember’s Convention over Configuratin philosophy.
1 2 3
I wanted to add additional todo objects on the fly so I wrote
addTodo event handler in the
IndexController. Nothing fancy. It creates and instance of
Ember.Object and pushes it onto
1 2 3 4 5 6 7 8 9 10 11 12
Now we’re cooking
Now comes the interesting part – computed properties. Let’s start with the last one:
1 2 3 4 5 6 7
On line 5 we grab the content array (remember it holds
filterBy("isDone", false) will return an array which only has objects whos
isDone property is set to
false. Finally we get the length of that array which corresponds to the remaining item count.
.property("content") do you know what
remainingThirdExample will return when we will add new object to the content array or change
isDone property on an existing object? Anyone?
Say we have two todos in the content array and for one of them
isDone property is set to
remainingThirdExample will return 1. Reason being we’re observing the array itself (
content) and not the elements in it.
Moving on to the
remainingSecondExample computed property:
1 2 3 4 5 6 7 8 9
.property("content.@each") basically means observe each element in the
content array. Ok, great. This must work because now instead of observing the
content array itself we’re looking for the changes in the array content. That’s at least what I was thinking. Turns out I was wrong. If you play with the Output window above you’ll notice that the count increments each time we add new todo but it doesn’t decrement when we (un)check some of the todos marking them (un)complete.
.property("content.@each") looks whether objects are being added or deleted from the
content array. That’s why it properly increments count when we add new object with
isDone property set to
false but doesn’t decrement the count when we mark todo as not completed by unchecking the checkbox.
Now, the version that works as expected:
1 2 3 4 5 6 7 8 9
By now you should see why
.property("content.@each.isDone") works. It observes
isDone property on each object that is in
content array and recalculates the remaining todos each time
isDone property gets changed in some object.
I didn’t touch the HTML part here because I think it’s pretty straight forward but please raise your voice in the comments in case something isn’t clear.
Also I did ask Luke and Stefan for their opinion on this article and they both pointed me to Ember.reduceComputed feature. At first I did want to change the existing code but then decided to write about in a separate blog post.
I really like Ember.js so I plan to write more about it in the near future.
I hope this was helpful. Till next post.