From 2e2344c5c029b78e1dcf1f7f7db91839caa0a10c Mon Sep 17 00:00:00 2001 From: Sergey Astapov Date: Wed, 23 Mar 2022 17:09:15 -0400 Subject: [PATCH 1/6] Element Modifiers --- text/0000-element-modifiers.md | 232 +++++++++++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 text/0000-element-modifiers.md diff --git a/text/0000-element-modifiers.md b/text/0000-element-modifiers.md new file mode 100644 index 0000000000..75d530c3e0 --- /dev/null +++ b/text/0000-element-modifiers.md @@ -0,0 +1,232 @@ +--- +Stage: Accepted +Start Date: +Release Date: Unreleased +Release Versions: + ember-source: vX.Y.Z + ember-data: vX.Y.Z +Relevant Team(s): Ember CLI, Learning +RFC PR: +--- + + + +# Element Modifiers + +## Summary + +This RFC introduces the concept of user defined element modifiers and proposes +adding [ember-modifier](https://github.com/ember-modifier/ember-modifier) +to the blueprints that back `ember new` and `ember addon`, providing an +officially-supported path for using modifiers out of the box. + +This RFC supersedes the original [RFC #353 "Modifiers"][rfc-0353]. + +## Motivation + +Ember Octane introduced Glimmer Components as a replacement for Classic +Components. They are simpler, more ergonomic, and more declarative. In contrast +with Classic Components, Glimmer Components don't have any element/DOM based +properties or hooks giving access to DOM. This was an intentional move as +component baking class gets disconnected from DOM manipulation. + +Modifiers are similar to template helpers: they are functions or classes that can be used +in templates directly using `{{double-curlies}}` syntax. The major difference with modifiers +is that they are applied directly to *elements*: + +```handlebars + +``` + +This RFC builds on top of low-level primitives for defining element modifiers +introduced in [RFC #373 "Element Modifier Manager"][rfc-0373]. +The `ember-modifier` addon was built on top of those low-level primitives and +provides a convenient API for authoring element modifiers in Ember. + +Element Modifiers were introduced as part of Ember Octane edition and are first class citizens +in Ember application, like components or helpers. However today, developers need to install +an additional library to be able to define custom modifiers. + +This RFC seeks to fill this gap in Ember.js' development mental model by +providing `ember-modifier` in the blueprint. + +A basic element modifier is defined with the type of `modifier`. +For example these paths would be global element modifiers in an application: + +* `app/modifiers/draggable.js` +* `app/modifiers/effect.js` + +Similar to helpers, modifiers can be function-based: + +```js +// /app/modifiers/on.js +import { modifier } from 'ember-modifier'; + +export default modifier((element, [eventName, handler]) => { + element.addEventListener(eventName, handler); + + return () => { + element.removeEventListener(eventName, handler); + } +}); +``` + +or class-based + +```ts +// /app/modifiers/on.js +import Modifier from 'ember-modifier'; +import { registerDestructor } from '@ember/destroyable'; + +function cleanup(instance: OnModifier) { + let { element, event, handler } = instance; + + if (element && event && handler) { + element.removeEventListener(event, handler); + + instance.element = null; + instance.event = null; + instance.handler = null; + } +} + +export default class OnModifier extends Modifier { + element = null; + event = null; + handler = null; + + // Manage teardown + constructor() { + super(...arguments); + registerDestructor(this, cleanup); + } + + // Run on installation and all changes to tracked state. + modify(element, [event, handler]) { + // Clear any previous state. + cleanup(this); + + // Set up the new handling. + this.addEventListener(element, event, handler); + } + + // methods for reuse + addEventListener(element, event, handler) { + // Store the current element, event, and handler for when we need to remove + // them during cleanup. + this.element = element; + this.event = event; + this.handler = handler; + + element.addEventListener(event, handler); + }; +} +``` + +While this is slightly more complicated than the function-based version, +that complexity comes along with much more *control*. It also allows us to hook +into Ember's dependency injection system, for example to use services. + +[RFC #432: Contextual Helpers and Modifiers][rfc-0432] allows optional +invocation and partial application of modifiers, and allows modifiers to be +yielded, passed as arguments, etc. [RFC #779: First-Class Component +Templates][rfc-0779] allows defining them locally or importing them directly to +be used in a `