{{component helper}}

By: Romina Vargas
component

At DockYard, one pattern that we’ve been noticing in some of our projects is needing to render different components based on some value. This value could come from a computed property, a model attribute, etc. Typically, this means that your template will contain some branching logic to figure out the appropriate template to render. A helpful helper that became available pre-Ember 2.0 is the {{component}}. With its help, we can clean up our template logic.

A distinguishing factor between the {{component}} helper and the traditional component invocation is that the helper expects the component name as the first parameter following the helper name, and will dynamically render out the component specified by that parameter. An arbitrary number of parameters may follow the component name; these are what the rendered component expects.

{{component myComponentName param2=param2 param3=param3 ...}}

Suppose we have a Food model. And each Food model instance has a taste attribute. We want to render a certain template depending on the string value of food.taste. Prior to having the {{component}} helper, we’d do something like the following:

// food/component.js
import Ember from 'ember';
const { Component, computed: { equal } } = Ember;

export default Component.extend({
  isSpicy: equal('food.taste', 'spicy'),
  isSweet: equal('food.taste', 'sweet')
});
{{! food/template.hbs }}

{{#if isSpicy}}
  {{food/spicy-food food=food}}
{{else if isSweet}}
  {{food/sweet-food food=food}}
{{else}}
  {{food/other-food food=food}}
{{/if}}

We defined a couple of computed properties and threw in some if statements in our template to dictate which component to render based on the food taste. But we can do better! Let’s see how our app cleans up when using the helper.

// food/component.js
import Ember from 'ember';
const { Component, computed, get } = Ember;

export default Component.extend({
  tastyComponentName: computed('food.taste', {
    get() {
      let foodTaste = get(this, 'food.taste');
      return `food/${foodTaste}-food`;
    }
  })
});
{{! food/template.hbs }}

{{component tastyComponentName food=food}}

Nice. Our template became a one-liner, and we were able to remove the computed properties that we had to define for each food taste that we wanted to render a separate component for. All of the logic is now consolidated into one computed that returns the name of the component that should be rendered. We can pass this property as the second argument inside {{component}}. If we wanted to later introduce new food tastes and components to go along with them, no changes would need to be made in these two files, since tastyComponentName takes care of all cases.

More powerful templates

In the above example, we are using the computed property tastyComponentName to name our template. But it’s not immediately obvious what the template actually is without looking at the computed property itself. We can forgo the CP altogether and use Handlebars subexpressions to keep all logic within the template.

Recently, Lauren and Marten released the ember-composable-helpers addon whose aim is to make logic in your Ember templates more declarative. How convenient that this addon complements {{component}} well! The helpers within ember-composable-helpers can be used in different combinations to format your component name depending on your needs. Furthermore, Ember itself ships with some built-in helpers that can be used inside our templates as well.

The Ember helper concat is one of the helpers we use fairly often. It’s common to name components based on a value of an attribute. In our example, the component name is based on food.taste. How would we modify our example to make use of the concat helper?

{{! food/template.hbs }}

{{component (concat "food/" food.taste "-food") food=food}}

And just like that, the food/component.js file is no longer needed. The concat helper replaces the computed property entirely. If the value of food.taste is sweet, then the helper will output food/sweet-food.

Another useful helper that works well in conjunction with concat is dasherize. This helper is part of ember-composable-helpers addon and will take care of lowercasing and dasherizing a given value. Sometimes, we have to work with camelized values, and using dasherize will convert them to a more desired format. Here’s an example of how the helpers would be used together:

{{component (concat "food/" (dasherize food.taste) "-food") food=food}}

In this case, if the value of food.taste is SuperSour, then our rendered component will be food/super-sour-food. With all the helpers we have available to us, think of all the combination possibilities!