An {{on}}
template helper complimentary to the
RFC #471 "{{on}}
modifier".
ember install ember-on-helper
- Ember.js v2.18 or above
- ember-cli v2.13 or above
You would use the {{on}}
modifier to register event listeners on elements
that are in the realm of your current template. But sometimes you need to
register event listeners on elements or even on generic EventTarget
s that are
outside of the control of your template, e.g. document
or window
.
⚠️ 👉 WARNING: Do not overuse this helper. If you want to bind to an element that is controlled by Glimmer, but maybe just not by the current template, do not reach for a manualdocument.querySelector()
. Instead, think about your current template and state setup and try to use a true "Data Down, Actions Up" pattern or use a sharedService
as a message bus.
Pretty much exactly the same as the {{on}}
modifier, except for that the
{{on}}
helper expects one more positional parameter upfront: the evenTarget
.
As with the {{on}}
modifier, you can also pass optional event options as named
parameters:
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class TomstersWitnessComponent extends Component {
document = document;
@action
onDocumentClick(event: MouseEvent) {
console.log(
'Do you have a minute to talk about our Lord and Savior, Ember.js?'
);
}
}
This is essentially equivalent to:
didInsertElement() {
super.didInsertElement();
document.addEventListener('click', this.onDocumentClick);
}
In addition to the above {{on}}
will properly tear down the event listener,
when the helper is removed from the DOM. It will also re-register the event
listener, if any of the passed parameters change.
The @action
decorator is used to bind the onDocumentClick
method
to the component instance. This is not strictly required here, since we do not
access this
, but in order to not break with established patterns, we do it
anyway.
You will often want to use the {{on}}
helper to listen to events which are
emitted on window
or document
. Because providing access to these globals in
the template as shown in Simple Example is quite
cumbersome, {{on}}
brings two friends to the party:
{{on-document eventName eventListener}}
{{on-window eventName eventListener}}
They work exactly the same way as {{on}}
and also accept event options.
You can use the {{on}}
helper multiple times in the same template and for the
same event target, even for the same event.
All named parameters will be passed through to
addEventListener
as the third parameter, the options hash.
This is essentially equivalent to:
didInsertElement() {
super.didInsertElement();
document.addEventListener('scroll', this.onScroll, { passive: true });
}
To fire an event listener only once, you can pass the once
option:
clickOnlyTheFirstTime
will only be fired the first time the page is clicked.
clickEveryTime
is fired every time the page is clicked, including the first
time.
To listen for an event during the capture phase already, use the capture
option:
If true
, you promise to not call event.preventDefault()
. This allows the
browser to optimize the processing of this event and not block the UI thread.
This prevent scroll jank.
If you still call event.preventDefault()
, an assertion will be raised.
Internet Explorer 11 has a buggy and incomplete implementation of
addEventListener
: It does not accept an
options
parameter and sometimes even throws
a cryptic error when passing options.
This is why this addon ships a tiny ponyfill for addEventLisener
that is used internally to emulate the once
, capture
and passive
option.
This means that all currently known options
are
polyfilled, so that you can rely on them in your logic.
If you want to curry the function call / partially apply arguments, you can do
so using the {{fn}}
helper:
import Component from '@ember/component';
import { action } from '@ember-decorators/object';
interface Video {
element: HTMLVideoElement;
title: string;
}
export default class UserListComponent extends Component {
videos: Video[];
@action
onPlay(video: Video, event: MouseEvent) {
console.log(`Started playing '${video.title}'.`);
}
@action
onPlay(video: Video, event: MouseEvent) {
console.log(`Paused '${video.title}'.`);
}
}
The old {{action}}
modifier used to allow easily
calling event.preventDefault()
like so:
You also could easily call event.stopPropagation()
to avoid bubbling like so:
You can still do this using ember-event-helpers
:
This addon is a straight copy of ember-on-modifier
, the
polyfill for the {{on}}
modifier.