Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/buschtoens/ember-on-helper
Complimentary `{{on}}` template helper for listening to events on `document`, `window` or other event targets
https://github.com/buschtoens/ember-on-helper
ember ember-addon ember-helper ember-template-helper emberjs
Last synced: 2 months ago
JSON representation
Complimentary `{{on}}` template helper for listening to events on `document`, `window` or other event targets
- Host: GitHub
- URL: https://github.com/buschtoens/ember-on-helper
- Owner: buschtoens
- License: mit
- Created: 2019-05-11T10:58:21.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-12-15T17:29:20.000Z (about 1 year ago)
- Last Synced: 2024-09-30T06:02:24.129Z (3 months ago)
- Topics: ember, ember-addon, ember-helper, ember-template-helper, emberjs
- Language: JavaScript
- Size: 642 KB
- Stars: 12
- Watchers: 3
- Forks: 1
- Open Issues: 25
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
Awesome Lists containing this project
README
# ember-on-helper
[![Build Status](https://travis-ci.org/buschtoens/ember-on-helper.svg)](https://travis-ci.org/buschtoens/ember-on-helper)
[![npm version](https://badge.fury.io/js/ember-on-helper.svg)](http://badge.fury.io/js/ember-on-helper)
[![Download Total](https://img.shields.io/npm/dt/ember-on-helper.svg)](http://badge.fury.io/js/ember-on-helper)
[![Ember Observer Score](https://emberobserver.com/badges/ember-on-helper.svg)](https://emberobserver.com/addons/ember-on-helper)
[![Ember Versions](https://img.shields.io/badge/Ember.js%20Versions-%5E2.18%20%7C%7C%20%5E3.0-brightgreen.svg)](https://travis-ci.org/buschtoens/ember-on-helper)
[![ember-cli Versions](https://img.shields.io/badge/ember--cli%20Versions-%5E2.13%20%7C%7C%20%5E3.0-brightgreen.svg)](https://travis-ci.org/buschtoens/ember-on-helper)
[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
[![dependencies](https://img.shields.io/david/buschtoens/ember-on-helper.svg)](https://david-dm.org/buschtoens/ember-on-helper)
[![devDependencies](https://img.shields.io/david/dev/buschtoens/ember-on-helper.svg)](https://david-dm.org/buschtoens/ember-on-helper)An `{{on}}` template helper complimentary to the
[RFC #471 "`{{on}}` modifier"](https://github.com/emberjs/rfcs/blob/master/text/0471-on-modifier.md).## Installation
```
ember install ember-on-helper
```#### Compatibility
- Ember.js v2.18 or above
- ember-cli v2.13 or above## But why?
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 manual `document.querySelector()`. Instead,
> think about your current template and state setup and try to use a true "Data
> Down, Actions Up" pattern or use a shared `Service` as a message bus.## Usage
Pretty much exactly the same as the `{{on}}` modifier, except for that the
`{{on}}` helper expects one more positional parameter upfront: the `evenTarget`.```hbs
{{on eventTarget eventName eventListener}}
```As with the `{{on}}` modifier, you can also pass optional event options as named
parameters:```hbs
{{on eventTarget eventName eventListener capture=bool once=bool passive=bool}}
```### Simple Example
```hbs
Click anywhere in the browser window, fam.{{on this.document "click" this.onDocumentClick}}
``````ts
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:
```ts
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][@action] 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.[@action]: https://github.com/emberjs/rfcs/blob/master/text/0408-decorators.md#method-binding
### Listening to Events on `window` or `document`
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](#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.
### Listening to Multiple Events
You can use the `{{on}}` helper multiple times in the same template and for the
same event target, even for the same event.```hbs
{{on this.someElement "click" this.onClick}}
{{on this.someElement "click" this.anotherOnClick}}
{{on this.someElement "mousemove" this.onMouseMove}}
```### Event Options
All named parameters will be passed through to
[`addEventListener`][addeventlistener] as the third parameter, the options hash.[addeventlistener]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
```hbs
{{on-document "scroll" this.onScroll passive=true}}
```This is essentially equivalent to:
```ts
didInsertElement() {
super.didInsertElement();document.addEventListener('scroll', this.onScroll, { passive: true });
}
```#### `once`
To fire an event listener only once, you can pass the [`once` option][addeventlistener-parameters]:
```hbs
{{on-window "click" this.clickOnlyTheFirstTime once=true}}
{{on-window "click" this.clickEveryTime}}
````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.[addeventlistener-parameters]: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Parameters
#### `capture`
To listen for an event during the capture phase already, use the [`capture` option][addeventlistener-parameters]:
```hbs
{{on-document "click" this.triggeredFirst capture=true}}Click me baby, one more time!
```
#### `passive`
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.
```hbs
{{on-document "scroll" this.trackScrollPosition passive=true}}>
```#### Internet Explorer 11 Support
Internet Explorer 11 has a buggy and incomplete implementation of
`addEventListener`: It does not accept an
[`options`][addeventlistener-parameters] parameter and _sometimes_ even throws
a cryptic error when passing options.This is why this addon ships a tiny [ponyfill][ponyfill] for `addEventLisener`
that is used internally to emulate the `once`, `capture` and `passive` option.
This means that all currently known [`options`][addeventlistener-parameters] are
polyfilled, so that you can rely on them in your logic.[ponyfill]: https://github.com/sindresorhus/ponyfill
### Currying / Partial Application
If you want to curry the function call / partially apply arguments, you can do
so using the [`{{fn}}` helper][fn-helper]:[fn-helper]: https://github.com/emberjs/rfcs/blob/master/text/0470-fn-helper.md
```hbs
{{#each this.videos as |video|}}
{{on video.element "play" (fn this.onPlay video)}}
{{on video.element "pause" (fn this.onPause video)}}
{{/each}}
``````ts
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}'.`);
}
}
```### `preventDefault` / `stopPropagation` / `stopImmediatePropagation`
The old [`{{action}}` modifier][action-event-propagation] used to allow easily
calling `event.preventDefault()` like so:```hbs
Click me
```[action-event-propagation]: https://www.emberjs.com/api/ember/release/classes/Ember.Templates.helpers/methods/action?anchor=action#event-propagation
You also could easily call `event.stopPropagation()` to avoid bubbling like so:
```hbs
Click me
```You can still do this using [`ember-event-helpers`][ember-event-helpers]:
[ember-event-helpers]: https://github.com/buschtoens/ember-event-helpers
```hbs
Click me
``````hbs
Click me
```## Attribution
This addon is a straight copy of [`ember-on-modifier`][ember-on-modifier], the
polyfill for the `{{on}}` modifier.[ember-on-modifier]: https://github.com/buschtoens/ember-on-modifier