6 Comments

Computed Properties with Computed Dependent Keys in Ember.js

Ember’s support for computed properties and bindings is excellent: powerful and not too complicated. Every so often, however, I find myself wanting a particular feature that is not built into Ember’s object system. I want the ability to declare a computed property whose dependent key(s) are actually the value of another computed property.

A pattern that I like to use is where I model important parts of my application in data, and use this data to programmatically accomplish things such as generating UI or controlling the behavior of the system. It’s at the level of writing that machinery that it becomes natural to leverage the computed properties system in more interesting ways.

Exploring the Problem

The intersection of the sets of people here at Atomic that also use Ember and that like to use data to drive their applications is steadily growing larger. Today, when faced with this problem yet again, I decided to ask around the office and see how other teams had handled it.

While I was chatting with Drew, we discussed a few ways that he had achieved this before on previous projects. The first time he’d solved this, he had used a combination of property observers and manually creating Ember.Binding objects. This was clever, but required a fair amount of code and had a drawback: the computed properties would not have a value immediately. Instead, you must wait until the second tick of the runloop after instantiation.

After this, he started to explain an alternative method that he’d used in another project, which led to my inspiration for how I ended up solving it for my project.

My Solution

You can’t naturally create properties with dynamic dependent keys in Ember because you have to know the key path at the time that you declare your computed property. However, there’s nothing stopping you from defining your own class and returning an instance of it in the body of a computed property. That’s the key here.

Let’s look at an example, where we want to depend on a subpath of our model that’s dynamically determined by the value of our “subpath” property:


Ember.Object.extend({
     trampoline: function() {
          return Ember.Object.extend({
               value: Ember.computed.alias(`model.${this.get("subpath")}`)
          }).create({model: this.get("model")});
     }.property("model", "subpath"),
     dynamicDependencyProp: Ember.computed.alias("trampoline.value")
});

As far as I can tell, this approach works quite well. Our property should behave just like any other computed property, without any caveats for the code outside this definition. The biggest drawback that I can see is that we need another property to be defined, which prevents us from wrapping this definition up into a convenient function for us to call.

Ways to Improve

Unfortunately, I don’t see any obvious ways to improve this. Ideally, you would not need the second computed property, which would not only make the implementation cleaner, but also allow for the definition to be abstracted into a function call that would return a computed property after doing the work for you.

I leave this as an exercise for the reader. Can you simplify or improve this technique?