Class Ember.Templates.helpers

public

Show:

Module: ember

The {{action}} helper provides a way to pass triggers for behavior (usually just a function) between components, and into components from controllers.

Passing functions with the action helper

There are three contexts an action helper can be used in. The first two contexts to discuss are attribute context, and Handlebars value context.

1
2
3
4
5
{{! An example of attribute context }}
<div onclick={{action "save"}}></div>
{{! Examples of Handlebars value context }}
{{input on-input=(action "save")}}
{{yield (action "refreshData") andAnotherParam}}

In these contexts, the helper is called a "closure action" helper. Its behavior is simple: If passed a function name, read that function off the actions property of the current context. Once that function is read, or immediately if a function was passed, create a closure over that function and any arguments. The resulting value of an action helper used this way is simply a function.

For example, in the attribute context:

1
2
{{! An example of attribute context }}
<div onclick={{action "save"}}></div>

The resulting template render logic would be:

1
2
3
4
5
6
7
var div = document.createElement('div');
var actionFunction = (function(context){
  return function() {
    return context.actions.save.apply(context, arguments);
  };
})(context);
div.onclick = actionFunction;

Thus when the div is clicked, the action on that context is called. Because the actionFunction is just a function, closure actions can be passed between components and still execute in the correct context.

Here is an example action handler on a component:

component.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class extends Component {
  @action
  save() {
    this.model.save();
  }
}

Actions are always looked up on the actions property of the current context. This avoids collisions in the naming of common actions, such as destroy. Two options can be passed to the action helper when it is used in this way.

  • target=someProperty will look to someProperty instead of the current context for the actions hash. This can be useful when targeting a service for actions.
  • value="target.value" will read the path target.value off the first argument to the action when it is called and rewrite the first argument to be that value. This is useful when attaching actions to event listeners.

Invoking an action

Closure actions curry both their scope and any arguments. When invoked, any additional arguments are added to the already curried list. Actions are presented in JavaScript as callbacks, and are invoked like any other JavaScript function.

For example

name.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class extends Component {
  @action
  setName(model, name) {
    model.set('name', name);
  }
}
name.hbs
1
{{input on-input=(action (action 'setName' @model) value="target.value")}}

The first argument (@model) was curried over, and the run-time argument (event) becomes a second argument. Action calls can be nested this way because each simply returns a function. Any function can be passed to the {{action}} helper, including other actions.

Actions invoked with sendAction have the same currying behavior as demonstrated with on-input above. For example:

input.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class extends Component {
  @action
  setName(model, name) {
    model.set('name', name);
  }
}
1
<MyInput @submit={{action 'setName' @model}} />

or

1
{{my-input submit=(action 'setName' @model)}}
component.js
1
2
3
4
5
6
7
8
import Component from '@ember/component';

export default Component.extend({
  click() {
    // Note that model is not passed, it was curried in the template
    this.submit('bob');
  }
});

Attaching actions to DOM elements

The third context of the {{action}} helper can be called "element space". For example:

1
2
{{! An example of element space }}
<div {{action "save"}}></div>

Used this way, the {{action}} helper provides a useful shortcut for registering an HTML element in a template for a single DOM event and forwarding that interaction to the template's context (controller or component). If the context of a template is a controller, actions used this way will bubble to routes when the controller does not implement the specified action. Once an action hits a route, it will bubble through the route hierarchy.

Event Propagation

{{action}} helpers called in element space can control event bubbling. Note that the closure style actions cannot.

Events triggered through the action helper will automatically have .preventDefault() called on them. You do not need to do so in your event handlers. If you need to allow event propagation (to handle file inputs for example) you can supply the preventDefault=false option to the {{action}} helper:

1
2
3
4
<div {{action "sayHello" preventDefault=false}}>
  <input type="file" />
  <input type="checkbox" />
</div>

To disable bubbling, pass bubbles=false to the helper:

1
<button {{action 'edit' post bubbles=false}}>Edit</button>

To disable bubbling with closure style actions you must create your own wrapper helper that makes use of event.stopPropagation():

1
<div onclick={{disable-bubbling (action "sayHello")}}>Hello</div>
bubbling.js
1
2
3
4
5
6
7
8
9
import { helper } from '@ember/component/helper';

export function disableBubbling([action]) {
  return function(event) {
    event.stopPropagation();
    return action(event);
  };
}
export default helper(disableBubbling);

If you need the default handler to trigger you should either register your own event handler, or use event methods on your view class. See "Responding to Browser Events" in the documentation for Component for more information.

Specifying DOM event type

{{action}} helpers called in element space can specify an event type. By default the {{action}} helper registers for DOM click events. You can supply an on option to the helper to specify a different DOM event name:

1
2
3
<div {{action "anActionName" on="doubleClick"}}>
  click me
</div>

See "Event Names" for a list of acceptable DOM event names.

Specifying whitelisted modifier keys

{{action}} helpers called in element space can specify modifier keys. By default the {{action}} helper will ignore click events with pressed modifier keys. You can supply an allowedKeys option to specify which keys should not be ignored.

1
2
3
<div {{action "anActionName" allowedKeys="alt"}}>
  click me
</div>

This way the action will fire when clicking with the alt key pressed down. Alternatively, supply "any" to the allowedKeys option to accept any combination of modifier keys.

1
2
3
<div {{action "anActionName" allowedKeys="any"}}>
  click me with any key pressed
</div>

Specifying a Target

A target option can be provided to the helper to change which object will receive the method call. This option must be a path to an object, accessible in the current context:

app/templates/application.hbs
1
2
3
<div {{action "anActionName" target=someService}}>
  click me
</div>
app/controllers/application.js
1
2
3
4
5
6
import Controller from '@ember/controller';
import { service } from '@ember/service';

export default class extends Controller {
  @service someService;
}
Module: ember

Available since v3.8.0

options
Array
returns
Array
Array

Use the {{array}} helper to create an array to pass as an option to your components.

1
2
3
4
5
<MyComponent @people={{array
  'Tom Dale'
  'Yehuda Katz'
  this.myOtherPerson}}
/>

or

1
2
3
4
5
{{my-component people=(array
  'Tom Dale'
  'Yehuda Katz'
  this.myOtherPerson)
}}

Would result in an object such as:

1
['Tom Dale', 'Yehuda Katz', this.get('myOtherPerson')]

Where the 3rd item in the array is bound to updates of the myOtherPerson property.

Module: ember

Available since v1.11.0

The {{component}} helper lets you add instances of Component to a template. See Component for additional information on how a Component functions. {{component}}'s primary use is for cases where you want to dynamically change which type of component is rendered as the state of your application changes. This helper has three modes: inline, block, and nested.

Inline Form

Given the following template:

app/application.hbs
1
{{component this.infographicComponentName}}

And the following application code:

app/controllers/application.js
1
2
3
4
5
6
7
8
9
10
import Controller from '@ember/controller';
import { tracked } from '@glimmer/tracking';

export default class ApplicationController extends Controller {
  @tracked isMarketOpen = 'live-updating-chart'

  get infographicComponentName() {
    return this.isMarketOpen ? 'live-updating-chart' : 'market-close-summary';
  }
}

The live-updating-chart component will be appended when isMarketOpen is true, and the market-close-summary component will be appended when isMarketOpen is false. If the value changes while the app is running, the component will be automatically swapped out accordingly. Note: You should not use this helper when you are consistently rendering the same component. In that case, use standard component syntax, for example:

app/templates/application.hbs
1
<LiveUpdatingChart />

or

app/templates/application.hbs
1
{{live-updating-chart}}

Block Form

Using the block form of this helper is similar to using the block form of a component. Given the following application template:

app/templates/application.hbs
1
2
3
{{#component this.infographicComponentName}}
  Last update: {{this.lastUpdateTimestamp}}
{{/component}}

The following controller code:

app/controllers/application.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class ApplicationController extends Controller {
  @tracked isMarketOpen = 'live-updating-chart'

  get lastUpdateTimestamp() {
    return new Date();
  }

  get infographicComponentName() {
    return this.isMarketOpen ? 'live-updating-chart' : 'market-close-summary';
  }
}

And the following component template:

chart.hbs
1
2
{{! chart }}
{{yield}}

The Last Update: {{this.lastUpdateTimestamp}} will be rendered in place of the {{yield}}.

Nested Usage

The component helper can be used to package a component path with initial attrs. The included attrs can then be merged during the final invocation. For example, given a person-form component with the following template:

form.hbs
1
2
3
{{yield (hash
  nameInput=(component "my-input-component" value=@model.name placeholder="First Name")
)}}

When yielding the component via the hash helper, the component is invoked directly. See the following snippet:

1
2
3
<PersonForm as |form|>
  <form.nameInput @placeholder="Username" />
</PersonForm>

or

1
2
3
{{#person-form as |form|}}
  {{form.nameInput placeholder="Username"}}
{{/person-form}}

Which outputs an input whose value is already bound to model.name and placeholder is "Username".

When yielding the component without the hash helper use the component helper. For example, below is a full-name component template:

1
{{yield (component "my-input-component" value=@model.name placeholder="Name")}}
1
2
3
<FullName as |field|>
  {{component field placeholder="Full name"}}
</FullName>

or

1
2
3
{{#full-name as |field|}}
  {{component field placeholder="Full name"}}
{{/full-name}}
Module: ember

Available since v1.13.0

Concatenates the given arguments into a string.

Example:

1
2
3
{{some-component name=(concat firstName " " lastName)}}

{{! would pass name="<first name value> <last name value>" to the component}}

or for angle bracket invocation, you actually don't need concat at all.

1
<SomeComponent @name="{{firstName}} {{lastName}}" />
Module: ember

Execute the debugger statement in the current template's context.

1
{{debugger}}

When using the debugger helper you will have access to a get function. This function retrieves values available in the context of the template. For example, if you're wondering why a value {{foo}} isn't rendering as expected within a template, you could place a {{debugger}} statement and, when the debugger; breakpoint is hit, you can attempt to retrieve this value:

1
> get('foo')

get is also aware of keywords. So in this situation

1
2
3
{{#each this.items as |item|}}
  {{debugger}}
{{/each}}

You'll be able to get values from the current item:

1
> get('item.name')

You can also access the context of the view to make sure it is the object that you expect:

1
> context
Module: ember

The {{#each}} helper loops over elements in a collection. It is an extension of the base Handlebars {{#each}} helper.

The default behavior of {{#each}} is to yield its inner block once for every item in an array passing the item as the first block parameter.

Assuming the @developers argument contains this array:

1
[{ name: 'Yehuda' },{ name: 'Tom' }, { name: 'Paul' }];
1
2
3
4
5
<ul>
  {{#each @developers as |person|}}
    <li>Hello, {{person.name}}!</li>
  {{/each}}
</ul>

The same rules apply to arrays of primitives.

1
['Yehuda', 'Tom', 'Paul']
1
2
3
4
5
<ul>
  {{#each @developerNames as |name|}}
    <li>Hello, {{name}}!</li>
  {{/each}}
</ul>

During iteration, the index of each item in the array is provided as a second block parameter.

1
2
3
4
5
<ul>
  {{#each @developers as |person index|}}
    <li>Hello, {{person.name}}! You're number {{index}} in line</li>
  {{/each}}
</ul>

Specifying Keys

In order to improve rendering speed, Ember will try to reuse the DOM elements where possible. Specifically, if the same item is present in the array both before and after the change, its DOM output will be reused.

The key option is used to tell Ember how to determine if the items in the array being iterated over with {{#each}} has changed between renders. By default the item's object identity is used.

This is usually sufficient, so in most cases, the key option is simply not needed. However, in some rare cases, the objects' identities may change even though they represent the same underlying data.

For example:

1
2
3
people.map(person => {
  return { ...person, type: 'developer' };
});

In this case, each time the people array is map-ed over, it will produce an new array with completely different objects between renders. In these cases, you can help Ember determine how these objects related to each other with the key option:

1
2
3
4
5
<ul>
  {{#each @developers key="name" as |person|}}
    <li>Hello, {{person.name}}!</li>
  {{/each}}
</ul>

By doing so, Ember will use the value of the property specified (person.name in the example) to find a "match" from the previous render. That is, if Ember has previously seen an object from the @developers array with a matching name, its DOM elements will be re-used.

There are two special values for key:

  • @index - The index of the item in the array.
  • @identity - The item in the array itself.

{{else}} condition

{{#each}} can have a matching {{else}}. The contents of this block will render if the collection is empty.

1
2
3
4
5
6
7
<ul>
  {{#each @developers as |person|}}
    <li>{{person.name}} is available!</li>
  {{else}}
    <li>Sorry, nobody is available for this task.</li>
  {{/each}}
</ul>
Module: ember

Available since v2.1.0

The {{each-in}} helper loops over properties on an object.

For example, given this component definition:

details.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class extends Component {
  @tracked developer = {
    "name": "Shelly Sails",
    "age": 42
  };
}

This template would display all properties on the developer object in a list:

details.hbs
1
2
3
4
5
<ul>
  {{#each-in this.developer as |key value|}}
    <li>{{key}}: {{value}}</li>
  {{/each-in}}
</ul>

Outputting their name and age:

1
2
3
4
<ul>
  <li>name: Shelly Sails</li>
  <li>age: 42</li>
</ul>
Module: ember

Available since v3.11.0

The fn helper allows you to ensure a function that you are passing off to another component, helper, or modifier has access to arguments that are available in the template.

For example, if you have an each helper looping over a number of items, you may need to pass a function that expects to receive the item as an argument to a component invoked within the loop. Here's how you could use the fn helper to pass both the function and its arguments together:

listing.hbs
1
2
3
{{#each @items as |item|}}
<DisplayItem @item=item @select={{fn this.handleSelected item}} />
{{/each}}
list.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class ItemsList extends Component {
  @action
  handleSelected(item) {
    // ...snip...
  }
}

In this case the display-item component will receive a normal function that it can invoke. When it invokes the function, the handleSelected function will receive the item and any arguments passed, thanks to the fn helper.

Let's take look at what that means in a couple circumstances:

  • When invoked as this.args.select() the handleSelected function will receive the item from the loop as its first and only argument.
  • When invoked as this.args.select('foo') the handleSelected function will receive the item from the loop as its first argument and the string 'foo' as its second argument.

In the example above, we used @action to ensure that handleSelected is properly bound to the items-list, but let's explore what happens if we left out @action:

list.js
1
2
3
4
5
6
7
import Component from '@glimmer/component';

export default class ItemsList extends Component {
  handleSelected(item) {
    // ...snip...
  }
}

In this example, when handleSelected is invoked inside the display-item component, it will not have access to the component instance. In other words, it will have no this context, so please make sure your functions are bound (via @action or other means) before passing into fn!

See also partial application.

Module: ember

Available since v2.1.0

Dynamically look up a property on an object or an element in an array. The second argument to {{get}} should have a string or number value, although it can be bound.

For example, these two usages are equivalent:

detail.js
1
2
3
4
5
6
7
8
9
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class extends Component {
  @tracked developer = {
    name: "Sandi Metz",
    language: "Ruby"
  }
}
1
2
{{this.developer.name}}
{{get this.developer "name"}}

If there were several facts about a person, the {{get}} helper can dynamically pick one:

app/templates/application.hbs
1
<DeveloperDetail @factName="language" />
1
{{get this.developer @factName}}

For a more complex example, this template would allow the user to switch between showing the user's name and preferred coding language with a click:

detail.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class extends Component {
  @tracked developer = {
    name: "Sandi Metz",
    language: "Ruby"
  }

  @tracked currentFact = 'name'

  @action
  showFact(fact) {
    this.currentFact = fact;
  }
}
detail.js
1
2
3
4
{{get this.developer this.currentFact}}

<button {{on 'click' (fn this.showFact "name")}}>Show name</button>
<button {{on 'click' (fn this.showFact "language")}}>Show language</button>

The {{get}} helper can also respect mutable values itself. For example:

detail.js
1
2
3
4
<Input @value={{mut (get this.person this.currentFact)}} />

<button {{on 'click' (fn this.showFact "name")}}>Show name</button>
<button {{on 'click' (fn this.showFact "language")}}>Show language</button>

Would allow the user to swap what fact is being displayed, and also edit that fact via a two-way mutable binding.

The {{get}} helper can also be used for array element access via index. This would display the value of the first element in the array this.names:

1
{{get this.names 0}}

Array element access also works with a dynamic second argument:

1
{{get this.names @index}}
Module: ember
the
String
name of the block. The name (at the moment) is either "main" or "inverse" (though only curly components support inverse)
returns
Boolean
`true` if the component was invoked with a block

{{(has-block)}} indicates if the component was invoked with a block.

This component is invoked with a block:

1
2
3
{{#my-component}}
  Hi Jen!
{{/my-component}}

This component is invoked without a block:

1
{{my-component}}

Using angle bracket invocation, this looks like:

1
<MyComponent>Hi Jen!</MyComponent> {{! with a block}}
1
<MyComponent/> {{! without a block}}

This is useful when you want to create a component that can optionally take a block and then render a default template when it is not invoked with a block.

component.hbs
1
2
3
4
5
{{#if (has-block)}}
  Welcome {{yield}}, we are happy you're here!
{{else}}
  Hey you! You're great!
{{/if}}
Module: ember
the
String
name of the block. The name (at the moment) is either "main" or "inverse" (though only curly components support inverse)
returns
Boolean
`true` if the component was invoked with block params

{{(has-block-params)}} indicates if the component was invoked with block params.

This component is invoked with block params:

1
2
3
{{#my-component as |favoriteFlavor|}}
  Hi Jen!
{{/my-component}}

This component is invoked without block params:

1
2
3
{{#my-component}}
  Hi Jenn!
{{/my-component}}

With angle bracket syntax, block params look like this:

1
2
3
<MyComponent as |favoriteFlavor|>
Hi Jen!
</MyComponent>

And without block params:

1
2
3
<MyComponent>
  Hi Jen!
</MyComponent>

This is useful when you want to create a component that can render itself differently when it is not invoked with block params.

component.hbs
1
2
3
4
5
6
7
{{#if (has-block-params)}}
  Welcome {{yield this.favoriteFlavor}}, we're happy you're here and hope you
  enjoy your favorite ice cream flavor.
{{else}}
  Welcome {{yield}}, we're happy you're here, but we're unsure what
  flavor ice cream you would enjoy.
{{/if}}
Module: ember

Available since v2.3.0

options
Object
returns
Object
Hash

Use the {{hash}} helper to create a hash to pass as an option to your components. This is specially useful for contextual components where you can just yield a hash:

1
2
3
4
{{yield (hash
   name='Sarah'
   title=office
)}}

Would result in an object such as:

1
{ name: 'Sarah', title: this.get('office') }

Where the title is bound to updates of the office property.

Note that the hash is an empty object with no prototype chain, therefore common methods like toString are not available in the resulting hash. If you need to use such a method, you can use the call or apply approach:

1
2
3
function toString(obj) {
  return Object.prototype.toString.apply(obj);
}
Module: ember

Available since v3.27.0

Use the {{helper}} helper to create contextual helper so that it can be passed around as first-class values in templates.

1
2
3
4
5
6
7
8
9
10
11
{{#let (helper "join-words" "foo" "bar" separator=" ") as |foo-bar|}}

  {{!-- this is equivalent to invoking `{{join-words "foo" "bar" separator=" "}}` --}}
  {{foo-bar}}

  {{!-- this will pass the helper itself into the component, instead of invoking it now --}}
  <MyComponent @helper={{helper foo-bar "baz"}} />

  {{!-- this will yield the helper itself ("contextual helper"), instead of invoking it now --}}
  {{yield foo-bar}}
{{/let}}

Arguments

The {{helper}} helper works similarly to the {{component}} and {{modifier}} helper:

  • When passed a string (e.g. (helper "foo")) as the first argument, it will produce an opaque, internal "helper definition" object that can be passed around and invoked elsewhere.

  • Any additional positional and/or named arguments (a.k.a. params and hash) will be stored ("curried") inside the definition object, such that, when invoked, these arguments will be passed along to the referenced helper.

Module: ember

The if helper allows you to conditionally render one of two branches, depending on the "truthiness" of a property. For example the following values are all falsey: false, undefined, null, "", 0, NaN or an empty array.

This helper has two forms, block and inline.

Block form

You can use the block form of if to conditionally render a section of the template.

To use it, pass the conditional value to the if helper, using the block form to wrap the section of template you want to conditionally render. Like so:

app/templates/application.hbs
1
<Weather />
app/components/weather.hbs
1
2
3
4
{{! will not render because greeting is undefined}}
{{#if @isRaining}}
  Yes, grab an umbrella!
{{/if}}

You can also define what to show if the property is falsey by using the else helper.

app/components/weather.hbs
1
2
3
4
5
{{#if @isRaining}}
  Yes, grab an umbrella!
{{else}}
  No, it's lovely outside!
{{/if}}

You are also able to combine else and if helpers to create more complex conditional logic.

For the following template:

app/components/weather.hbs
1
2
3
4
5
6
7
{{#if @isRaining}}
 Yes, grab an umbrella!
{{else if @isCold}}
 Grab a coat, it's chilly!
{{else}}
 No, it's lovely outside!
{{/if}}

If you call it by saying isCold is true:

app/templates/application.hbs
1
<Weather @isCold={{true}} />

Then Grab a coat, it's chilly! will be rendered.

Inline form

The inline if helper conditionally renders a single property or string.

In this form, the if helper receives three arguments, the conditional value, the value to render when truthy, and the value to render when falsey.

For example, if useLongGreeting is truthy, the following:

app/templates/application.hbs
1
<Greeting @useLongGreeting={{true}} />
app/components/greeting.hbs
1
{{if @useLongGreeting "Hello" "Hi"}} Alex

Will render:

1
Hello Alex

One detail to keep in mind is that both branches of the if helper will be evaluated, so if you have {{if condition "foo" (expensive-operation "bar"), expensive-operation will always calculate.

Module: ember

The in-element helper renders its block content outside of the regular flow, into a DOM element given by its destinationElement positional argument.

Common use cases - often referred to as "portals" or "wormholes" - are rendering dropdowns, modals or tooltips close to the root of the page to bypass CSS overflow rules, or to render content to parts of the page that are outside of the control of the Ember app itself (e.g. embedded into a static or server rendered HTML page).

1
2
3
{{#in-element this.destinationElement}}
  <div>Some content</div>
{{/in-element}}

Arguments

{{in-element}} requires a single positional argument:

  • destinationElement -- the DOM element to render into. It must exist at the time of rendering.

It also supports an optional named argument:

  • insertBefore -- by default the DOM element's content is replaced when used as destinationElement. Passing null changes the behaviour to appended at the end of any existing content. Any other value than null is currently not supported.
Module: ember
options
Hash

See Ember.Templates.components.Input.

Module: ember

The let helper receives one or more positional arguments and yields them out as block params.

This allows the developer to introduce shorter names for certain computations in the template.

This is especially useful if you are passing properties to a component that receives a lot of options and you want to clean up the invocation.

For the following example, the template receives a post object with content and title properties.

We are going to call the my-post component, passing a title which is the title of the post suffixed with the name of the blog, the content of the post, and a series of options defined in-place.

1
2
3
4
5
6
7
8
9
10
11
{{#let
    (concat post.title ' | The Ember.js Blog')
    post.content
    (hash
      theme="high-contrast"
      enableComments=true
    )
    as |title content options|
}}
  <MyPost @title={{title}} @content={{content}} @options={{options}} />
{{/let}}

or

1
2
3
4
5
6
7
8
9
10
11
{{#let
  (concat post.title ' | The Ember.js Blog')
  post.content
  (hash
    theme="high-contrast"
    enableComments=true
  )
  as |title content options|
}}
{{my-post title=title content=content options=options}}
{{/let}}
Module: ember

See Ember.Templates.components.LinkTo.

Module: ember
params
Array

log allows you to output the value of variables in the current rendering context. log also accepts primitive types such as strings or numbers.

1
{{log "myVariable:" myVariable }}
Module: ember

Available since v3.27.0

Use the {{modifier}} helper to create contextual modifier so that it can be passed around as first-class values in templates.

1
2
3
4
5
6
7
8
9
10
11
{{#let (modifier "click-outside" click=this.submit) as |on-click-outside|}}

  {{!-- this is equivalent to `<MyComponent {{click-outside click=this.submit}} />` --}}
  <MyComponent {{on-click-outside}} />

  {{!-- this will pass the modifier itself into the component, instead of invoking it now --}}
  <MyComponent @modifier={{modifier on-click-outside "extra" "args"}} />

  {{!-- this will yield the modifier itself ("contextual modifier"), instead of invoking it now --}}
  {{yield on-click-outside}}
{{/let}}

Arguments

The {{modifier}} helper works similarly to the {{component}} and {{helper}} helper:

  • When passed a string (e.g. (modifier "foo")) as the first argument, it will produce an opaque, internal "modifier definition" object that can be passed around and invoked elsewhere.

  • Any additional positional and/or named arguments (a.k.a. params and hash) will be stored ("curried") inside the definition object, such that, when invoked, these arguments will be passed along to the referenced modifier.

Module: ember
name
String
Name of the engine to mount.
model
Object
Object that will be set as the model of the engine.

The {{mount}} helper lets you embed a routeless engine in a template. Mounting an engine will cause an instance to be booted and its application template to be rendered.

For example, the following template mounts the ember-chat engine:

1
2
{{! application.hbs }}
{{mount "ember-chat"}}

Additionally, you can also pass in a model argument that will be set as the engines model. This can be an existing object:

1
2
3
<div>
  {{mount 'admin' model=userSettings}}
</div>

Or an inline hash, and you can even pass components:

1
2
3
4
5
6
7
<div>
  <h1>Application template!</h1>
  {{mount 'admin' model=(hash
      title='Secret Admin'
      signInButton=(component 'sign-in-button')
  )}}
</div>
Module: ember
attr
Object
the "two-way" attribute that can be modified.

The mut helper lets you clearly specify that a child Component can update the (mutable) value passed to it, which will change the value of the parent component.

To specify that a parameter is mutable, when invoking the child Component:

1
<MyChild @childClickCount={{fn (mut totalClicks)}} />

or

1
{{my-child childClickCount=(mut totalClicks)}}

The child Component can then modify the parent's value just by modifying its own property:

1
2
3
4
5
6
// my-child.js
export default Component.extend({
  click() {
    this.incrementProperty('childClickCount');
  }
});

Note that for curly components ({{my-component}}) the bindings are already mutable, making the mut unnecessary.

Additionally, the mut helper can be combined with the fn helper to mutate a value. For example:

1
<MyChild @childClickCount={{this.totalClicks}} @click-count-change={{fn (mut totalClicks))}} />

or

1
{{my-child childClickCount=totalClicks click-count-change=(fn (mut totalClicks))}}

The child Component would invoke the function with the new click value:

1
2
3
4
5
6
// my-child.js
export default Component.extend({
  click() {
    this.get('click-count-change')(this.get('childClickCount') + 1);
  }
});

The mut helper changes the totalClicks value to what was provided as the fn argument.

The mut helper, when used with fn, will return a function that sets the value passed to mut to its first argument. As an example, we can create a button that increments a value passing the value directly to the fn:

1
2
3
4
{{! inc helper is not provided by Ember }}
<button onclick={{fn (mut count) (inc count)}}>
  Increment count
</button>
Module: ember

Available since v3.11.0

The {{on}} modifier lets you easily add event listeners (it uses EventTarget.addEventListener internally).

For example, if you'd like to run a function on your component when a <button> in the components template is clicked you might do something like:

post.hbs
1
<button {{on 'click' this.saveLike}}>Like this post!</button>
post.js
1
2
3
4
5
6
7
8
9
10
import Component from '@glimmer/component';
import { action } from '@ember/object';

export default class LikePostComponent extends Component {
  @action
  saveLike() {
    // someone likes your post!
    // better send a request off to your server...
  }
}

Arguments

{{on}} accepts two positional arguments, and a few named arguments.

The positional arguments are:

  • event -- the name to use when calling addEventListener
  • callback -- the function to be passed to addEventListener

The named arguments are:

  • capture -- a true value indicates that events of this type will be dispatched to the registered listener before being dispatched to any EventTarget beneath it in the DOM tree.
  • once -- indicates that the listener should be invoked at most once after being added. If true, the listener would be automatically removed when invoked.
  • passive -- if true, indicates that the function specified by listener will never call preventDefault(). If a passive listener does call preventDefault(), the user agent will do nothing other than generate a console warning. See Improving scrolling performance with passive listeners to learn more.

The callback function passed to {{on}} will receive any arguments that are passed to the event handler. Most commonly this would be the event itself.

If you would like to pass additional arguments to the function you should use the {{fn}} helper.

For example, in our example case above if you'd like to pass in the post that was being liked when the button is clicked you could do something like:

post.hbs
1
<button {{on 'click' (fn this.saveLike @post)}}>Like this post!</button>

In this case, the saveLike function will receive two arguments: the click event and the value of @post.

Function Context

In the example above, we used @action to ensure that likePost is properly bound to the items-list, but let's explore what happens if we left out @action:

post.js
1
2
3
4
5
6
7
import Component from '@glimmer/component';

export default class LikePostComponent extends Component {
  saveLike() {
    // ...snip...
  }
}

In this example, when the button is clicked saveLike will be invoked, it will not have access to the component instance. In other words, it will have no this context, so please make sure your functions are bound (via @action or other means) before passing into on!

Module: ember

The {{outlet}} helper lets you specify where a child route will render in your template. An important use of the {{outlet}} helper is in your application's application.hbs file:

app/templates/application.hbs
1
2
3
4
5
6
7
8
<MyHeader />

<div class="my-dynamic-content">
  <!-- this content will change based on the current route, which depends on the current URL -->
  {{outlet}}
</div>

<MyFooter />

See the routing guide for more information on how your route interacts with the {{outlet}} helper. Note: Your content will not render if there isn't an {{outlet}} for it.

Module: ember
param
String

page-title allows you to set the title of any page in your application and append additional titles for each route. For complete documentation, see https://github.com/ember-cli/ember-page-title.

1
{{page-title "My Page Title" }}
Module: ember

See Ember.Templates.components.Textarea.

Module: ember

The {{unbound}} helper disconnects the one-way binding of a property, essentially freezing its value at the moment of rendering. For example, in this example the display of the variable name will not change even if it is set with a new value:

1
{{unbound this.name}}

Like any helper, the unbound helper can accept a nested helper expression. This allows for custom helpers to be rendered unbound:

1
2
3
4
{{unbound (some-custom-helper)}}
{{unbound (capitalize this.name)}}
{{! You can use any helper, including unbound, in a nested expression }}
{{capitalize (unbound this.name)}}

The unbound helper only accepts a single argument, and it return an unbound value.

Module: ember

Available since v4.4.0

Use the {{unique-id}} helper to generate a unique ID string suitable for use as an ID attribute in the DOM.

1
<input id={{unique-id}} type="email" />

Each invocation of {{unique-id}} will return a new, unique ID string. You can use the let helper to create an ID that can be reused within a template.

1
2
3
4
{{#let (unique-id) as |emailId|}}
  <label for={{emailId}}>Email address</label>
  <input id={{emailId}} type="email" />
{{/let}}
Module: ember

The unless helper is the inverse of the if helper. It displays if a value is falsey ("not true" or "is false"). Example values that will display with unless: false, undefined, null, "", 0, NaN or an empty array.

Inline form

The inline unless helper conditionally renders a single property or string. This helper acts like a ternary operator. If the first property is falsy, the second argument will be displayed, otherwise, the third argument will be displayed

For example, if you pass a falsey useLongGreeting to the Greeting component:

app/templates/application.hbs
1
<Greeting @useLongGreeting={{false}} />
app/components/greeting.hbs
1
{{unless @useLongGreeting "Hi" "Hello"}} Ben

Then it will display:

1
Hi Ben

Block form

Like the if helper, the unless helper also has a block form.

The following will not render anything:

app/templates/application.hbs
1
<Greeting />
app/components/greeting.hbs
1
2
3
{{#unless @greeting}}
  No greeting was found. Why not set one?
{{/unless}}

You can also use an else helper with the unless block. The else will display if the value is truthy.

If you have the following component:

in.hbs
1
2
3
4
5
{{#unless @userData}}
  Please login.
{{else}}
  Welcome back!
{{/unless}}

Calling it with a truthy userData:

app/templates/application.hbs
1
<LoggedIn @userData={{hash username="Zoey"}} />

Will render:

1
Welcome back!

and calling it with a falsey userData:

app/templates/application.hbs
1
<LoggedIn @userData={{false}} />

Will render:

1
Please login.
Module: ember
options
Hash
returns
String
HTML string

{{yield}} denotes an area of a template that will be rendered inside of another template.

Use with Component

When designing components {{yield}} is used to denote where, inside the component's template, an optional block passed to the component should render:

app/templates/application.hbs
1
2
3
<LabeledTextfield @value={{@model.name}}>
  First name:
</LabeledTextfield>
textfield.hbs
1
2
3
<label>
  {{yield}} <Input @value={{@value}} />
</label>

Result:

1
2
3
<label>
  First name: <input type="text" />
</label>

Additionally you can yield properties into the context for use by the consumer:

app/templates/application.hbs
1
2
3
4
5
6
<LabeledTextfield @value={{@model.validation}} @validator={{this.firstNameValidator}} as |validationError|>
  {{#if validationError}}
    <p class="error">{{validationError}}</p>
  {{/if}}
  First name:
</LabeledTextfield>
textfield.hbs
1
2
3
<label>
  {{yield this.validationError}} <Input @value={{@value}} />
</label>

Result:

1
2
3
4
<label>
  <p class="error">First Name must be at least 3 characters long.</p>
  First name: <input type="text" />
</label>