Helper Functions

Helper functions are JavaScript functions that you can call from your template.

Ember's template syntax limits what you can express to keep the structure of your application clear at a glance. When you need to compute something using JavaScript, you can use helper functions.

For instance, let's take a look at a generic message component from a messaging app.

<Message::Avatar
  @title={{@avatarTitle}}
  @initial={{@avatarInitial}}
  @isActive={{@userIsActive}}
  class={{if @isCurrentUser "current-user"}}
/>
<section>
  <Message::Username
    @name={{@username}}
    @localTime={{@userLocalTime}}
  />

  {{yield}}
</section>
<Message
  @username="Tomster"
  @userIsActive={{true}}
  @userLocalTime="4:56pm"
  @avatarTitle="Tomster's avatar"
  @avatarInitial="T"
>
  <p>
    Hey Zoey, have you had a chance to look at the EmberConf
    brainstorming doc I sent you?
  </p>
</Message>

By looking at how we use the <Message> component, we can see that some of the arguments are fairly repetitive. Both @avatarTitle and @avatarInitial are based on the user's @username, but the title has more text, and the initial is only the first letter of the name. We'd rather just pass a username to the <Message> component and compute the value of the title and initial.

Let's update the component to do that. It'll take a @username argument and calculate the title and initial.

Since the title is just the @username plus some extra stuff, we can replace @avatarTitle by interpolating the @username argument in a string literal passed to <Message::Avatar>.

<Message::Avatar
  @title={{@avatarTitle}}
  @title="{{@username}}'s avatar"
  @initial={{@avatarInitial}}
  @isActive={{@userIsActive}}
  class={{if @isCurrentUser "current-user"}}
/>
<section>
  <Message::Username
    @name={{@username}}
    @localTime={{@userLocalTime}}
  />

  {{yield}}
</section>

However, to get the first initial of the string, we'll need to use JavaScript. To do that, we'll write a helper function.

Writing a Helper Function

We define helper functions in the app/helpers folder.

In this case we want a helper function that takes three arguments: a string, a starting position, and a length. The function will return a substring of the original string.

To implement the helper, we write a JavaScript function that takes its arguments as an array. This is because helpers can also receive named arguments, which we'll discuss next.

import { helper } from '@ember/component/helper';

function substring(args) {
  let [string, start, end] = args;
  return string.substring(start, end);
}

export default helper(substring);

We can tighten up the implementation by moving the destructuring into the function's signature.

import { helper } from '@ember/component/helper';

function substring([string, start, end]) {
function substring(args) {
  let [string, start, end] = args;
  return string.substring(start, end);
}

export default helper(substring);

This is how we normally write helpers in Ember.

We can then use this helper in the component's template to get the first letter of the username.

<Message::Avatar
  @title="{{@username}}'s avatar"
  @initial={{@avatarTitle}}
  @initial={{substring @username 0 1}}
  @isActive={{@userIsActive}}
  class={{if @isCurrentUser "current-user"}}
/>
<section>
  <Message::Username
    @name={{@username}}
    @localTime={{@userLocalTime}}
  />

  {{yield}}
</section>

Named Arguments

The syntax {{substring @username 0 1}} is a little hard to read. We see some numbers at the end but can't tell what exactly they mean. We can use named arguments to make the substring helper easier to read.

Using named arguments, we could make our template a lot clearer.

<Message::Avatar
  @title="{{@username}}'s avatar"
  @initial={{substring @username 0 1}}
  {{! This won't work yet! We need to update the substring helper }}
  @initial={{substring @username start=0 end=1}}
  @isActive={{@userIsActive}}
  class={{if @isCurrentUser "current-user"}}
/>
<section>
  <Message::Username
    @name={{@username}}
    @localTime={{@userLocalTime}}
  />

  {{yield}}
</section>

In addition to taking positional arguments as an array, helpers take named arguments as a JavaScript object. Here's what that looks like using destructuring syntax.

import { helper } from '@ember/component/helper';

function substring([string], { start, end }) {
  return string.substring(start || 0, end);
}

export default helper(substring);

You can mix positional and named arguments to make your templates easy to read.

Nested Helpers

Sometimes, you might see helpers invoked by placing them inside parentheses, (). This means that a Helper is being used inside of another Helper or Component. This is referred to as a "nested" Helper Invocation. Parentheses must be used because curly braces {{}} cannot be nested.

{{sum (multiply 2 4) 2}}

In this example, we are using a helper to multiply 2 and 4 before passing the value into {{sum}}.

Thus, the output of these combined helpers is 10.

As you move forward with these template guides, keep in mind that a helper can be used anywhere a normal value can be used.

Many of Ember's built-in helpers (as well as your custom helpers) can be used in nested form.

Advanced: Class Helpers

Helpers can also be defined using class syntax. For instance, we could define the substring helper using classes instead.

import { helper } from '@ember/component/helper';
import Helper from '@ember/component/helper';

function substring([string], { start, length }) {
export default class Substring extends Helper {
  compute([string], { start, end }) {
    return string.substring(start || 0, end);
  }
}

Class helpers are useful when the helper logic is fairly complicated, requires fine-grained control of the helper lifecycle, or is stateful (we'll be discussing state in the next chapter).

Built-in Helpers

The get helper

The {{get}} helper makes it easy to dynamically send the value of a variable to another helper or component. This can be useful if you want to output one of several values based on the result of a getter.

{{get this.address this.part}}

If the part getter returns "zip", this will display the result of this.address.zip. If it returns "city", you get this.address.city.

The concat helper

We mentioned above that helpers can be nested. This can be combined with different dynamic helpers. For example, the {{concat}} helper makes it easy to dynamically send a number of parameters to a component or helper as a single parameter in the format of a concatenated string.

{{get this.foo (concat "item" this.index)}}

This will display the result of this.foo.item1 when index is 1, and this.foo.item2 when index is 2, etc.

The let helper

Now let's say your template is starting to get a bit cluttered and you want to clean up the logic in your templates. This can be achieved with the let block helper. The {{let}} helper lets you create new bindings (or temporary variables) in your template.

Say your template now looks like this:

Welcome back {{concat (capitalize this.person.givenName) ' ' (capitalize this.person.familyName)}}

Account Details:
Given Name: {{capitalize this.person.givenName}}
Family Name: {{capitalize this.person.familyName}}

As mentioned in the previous section, we use the concat helper to render both person.givenName and person.familyName in one go. But we also want to make sure that the names are capitalized. It gets a bit repetitive to keep writing capitalize and honestly, we might just forget it at some point. Thankfully, we can use the {{let}} helper to fix this:

{{#let (capitalize this.person.givenName) (capitalize this.person.familyName)
  as |givenName familyName|
}}
  Welcome back {{concat givenName ' ' familyName}}

  Account Details:
  Given Name: {{givenName}}
  Family Name: {{familyName}}
{{/let}}

Now, as long as your template is wrapped in the let helper, you can access the capitalized given name and family name as givenName and familyName instead of (capitalize this.person.givenName).

The array helper

Using the {{array}} helper, you can pass arrays directly from the template as an argument to your components.

<MyComponent
  @people={{array
    'Tom Dale'
    'Yehuda Katz'
    this.myOtherPerson
  }}
/>

In the component's template, you can then use the people argument as an array:

<ul>
  {{#each @people as |person|}}
    <li>{{person}}</li>
  {{/each}}
</ul>

The hash helper

Using the {{hash}} helper, you can pass objects directly from the template as an argument to your components.

<Greeting
  @person={{hash
    givenName='Jen'
    familyName='Weber'
  }}
/>

In the component's template, you can then use the person object:

Hello, {{@person.givenName}} {{@person.familyName}}

To consult all available built-in helpers, you can check the template helpers API documentation.

© 2020 Yehuda Katz, Tom Dale and Ember.js contributors
Licensed under the MIT License.
https://guides.emberjs.com/v3.25.0/components/helper-functions