On Modifier: A first look

Learn about the newest – and hopefully last! – way to handle events in Ember. Octane approved.

Summary

Let's experiment with the new {{on}} modifier that shipped with Ember 3.11, and is the new recommended way to handle events in Ember.

We'll take a look at an existing template in EmberMap's code base:

<button onclick={{action 'increasePlaybackRate'}}>
  {{fa-icon 'plus'}}
</button>

Currently, we're using closure actions to wire up this click event.

Closure actions are one of two existing ways to wire up actions in Ember. The other way is using the action modifier:

<button {{action 'increasePlaybackRate'}}>
  {{fa-icon 'plus'}}
</button>

The action modifier is the original way events were handled in Ember, and it has some drawbacks:

  • it binds the correct this context in a magical and non-intuitive way
  • it listens for the click event by default, and uses a strange on="mouseenter" option to allow users to change the event
  • it's become more confusing with the addition of the action helper, which has completely different functionality from the action modifier

When closure actions were introduced, they improved on the action modifier in many ways. But they too have some drawbacks:

  • the <div onclick={{action 'handleClick'}}> syntax works because of an inconsistent DOM API, reflection. The template is setting the onclick attribute, which is then reflected to the actual DOM node's onclick property. This ambiguity causes a few problems: it doesn't work the same for all events, and it's not robust to server-side rendering.

The {{on}} modifier was introduced to address these shortcomings. It looks like this:

<button {{on 'click' (action 'increasePlaybackRate')}}>
  {{fa-icon 'plus'}}
</button>

and it's effectively sugar for the addEventListener API:

buttonEl.addEventListener('click', this.increasePlaybackRate);

There are several benefits to {{on}}:

  • it's explicit
  • it works consistently for all events
  • it works with web components
  • it is robust to SSR, because all modifiers are stripped out and ignored in a server-side environment
  • it can be used multiple times

The biggest thing to note is that on does not bind this context. It's important to understand this, and the role it will play along with the other new primitives coming to Octane, notably the action decorator and the fn modifier. Those will be addressed in a future video.

on is solely about setting up (and tearing down) event listeners via the addEventListener API, all from templates. action can still be used to bind the correct context.

<button {{on 'click' (action 'increasePlaybackRate')}}>
  {{fa-icon 'plus'}}
</button>

So give the new on modifier a shot in your own apps! I think you'll appreciate the clarity and explicitness of this new modifier.


Here's some links for reference:

👋 Hey there, Ember dev!

We hope you enjoyed this free video 🙂

If you like it and want to keep learning with us, we've written a free 6-lesson email course about the fundamental patterns of modern component design in Ember.

To get the first lesson now, enter your best email address below:

You can also check out more details about the course by clicking here.

Questions?

Send us a tweet:

Or ask us in Inside EmberMap, our private Slack workspace for subscribers.