EmberJS has a lot of features for helping you build a clean JavaScript interface. One of my favorites is the computed property; Ember can watch a property or set of properties and when any of those change, it recalculates a value that is currently displayed on a screen:
fullName: (->
"#{@get('firstName)} #{@get('lastName')}"
).property('firstName', 'lastName')
Any time the firstName
or lastName
of the current object change,
the fullName
property will also be updated.
In my last project I needed to cacluate the sum of a few properties in an array. I started with a computed property:
sumOfCost: (->
@reduce ((previousValue, element) ->
currentValue = element.get('cost')
if isNaN(currentValue)
previousValue
else
previousValue + currentValue
), 0
).property('@each.cost')
This works fine but I need to use this same function for a number of different properties on this controller as well as others. As such, I extracted a helper function:
# math_helpers.js.coffee
class App.mathHelpers
@sumArrayForProperty: (array, propertyName) ->
array.reduce ((previousValue, element) ->
currentValue = element.get(propertyName)
if isNaN(currentValue)
previousValue
else
previousValue + currentValue
), 0
# array_controller.js.coffee
sumOfCost: (->
App.mathHelpers.sumArrayForProperty(@, 'cost')
).property('@each.cost')
This removes a lot of duplication but I still have the cost
property name in
the helper method as well as the property declaration. I also have the
‘decoration’ of setting up a computed property in general.
What I need is something that works like Ember.computed.alias('name')
but
allows me to transform the object instead of just aliasing a property:
# computed_properties.js.coffee
App.computed = {}
App.computed.sumByProperty = (propertyName) ->
Ember.computed "@each.#{propertyName}", ->
App.mathHelpers.sumArrayForProperty(@, propertyName)
# array_controller.js.coffee
sumOfCost: App.computed.sumByProperty('cost')
This allows me to easily define a ‘sum’ for a property without a lot of duplication. In this application I have a lot of similar functions around computing information in arrays. Being able to easily have one function for calculation allowed me to easily unit test that function and feel confident that it would work on any other object. It also simplifies the model or controller a lot for anyone viewing the class for the first time.
Update
After publishing this, I was quickly notified that Ember.computed.sum
has been
added to Ember as of 1.4. This is a great addition to the
core, but this example is primarily designed to show an example of how to
refactor your own computed property.