The Worry Free Labs
development team loves Ember.js and one of the things we love about it is that it's opinionated on how your software should be be built. Unfortunately, that can also lead to some major frustration as you try to figure out "the Ember way" of doing things. One such problem came up this week and we wanted to share our solution.
We're building an app that needs to dynamically update a list of service add-ons from an API as the user changes their selections. In the Ruby on Rails
or other server-side tool, the list of of available add-ons would be easy to manage using view helpers, but because of how Ember handles caching of computed properties, you have to think about things differently.
The following handlebars code shows what we're going for.
The problem occurs on line 6 since there's no way to do that in default Handlebars or Ember.
First Attempt: Custom Handlebars Helpers
If we were building this app in Rails, our first thought would be to write a custom helper to see if the current element is in the array of target elements. Based on that, we started looking in to how to build a custom Handlebars helper to perform the task and ended up with the following.
This actually worked perfectly, but had one major problem, it wasn't bound to the array properties so it didn't update when things changed. To make it bound, we'd need to use
instead. Unfortunately, when we made that switch Ember got mad at us.
registerBoundHelper-generated helpers do not support use with Handlebars blocks
Sure enough, the docs say the same thing:
Bound helpers do not support use with Handlebars blocks or the addition of child views of any kind.
Time for Plan B.
Finding the Ember Way
We knew this couldn't be a unique problem but all of our Googling came back to the same answer: find a way to make a computed property that doesn't need an argument. We looked at the data model again and couldn't figure out where to add such a computed property. As we dug more, we realized we were missing one of Ember's key principles: the controllers are the models, but Ember Data just represents your APIs. Stepping up a tier from the raw data level, we started looking at how data was represented in the controllers and realized we could leverage an item controller to do the hard work.
Lots of tinkering and trials finally brought us to a working solution:
Now the buttons show and hide as the list of available add-ons changes, exactly the way we want it to. Unfortunately, our buttons don't work anymore. We originally defined the
action within the controller for the overall view (i.e. the controller mapped to the service itself). Since the action method is now being called within the scope block our new controller, we need to make a slight adjustment to get it working again.
Also note that you don't have to use the
approach here. You could just as easily set
loop, but this is a slightly simplified version of our actual problem where that didn't work and I wanted to share the scoping approach.
Ember's approach can be a challenge sometimes and requires you to change the way you think about how to treat more complicated computed properties. It's also important to think about how to leverage your controllers to make your templates simpler.