-
-
Notifications
You must be signed in to change notification settings - Fork 408
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Modifiers #353
Modifiers #353
Conversation
3b52b8e
to
fa68f50
Compare
@chadhietala what does this mean for overloaded Like Is that something we want to allow as public API for other users? How do we keep that compatible with current helpers and AST transforms like https://github.com/mmun/ember-component-attributes? |
Do you have any concerns around making it easy to static-analyze potential misuse of helpers and modifiers? As it stands, we'd have to know more about the implementation of these two things ( <button onClick={{my-modifier "does not emit a value"}} > <button {{my-helper "emit a value into nothingness"}}> |
text/0000-modifiers.md
Outdated
This hook has the following timing semantics: | ||
|
||
**Always** | ||
- called **after** any children modifiers `didInsertElement` hook are called |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'any' => 'all'?
text/0000-modifiers.md
Outdated
This hook has the following timing semantics: | ||
|
||
**Always** | ||
- called **after** any children modifier's `willDestroyElement` hook is called |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'any' => 'all'? or before any children. willDestroyElement
sounds more like a hook called before the recursive destruction.
@mike-north this is going to result in a compilation error. In the case of the modifier you would get an error like
In the case of the modifier being used outside of element space you will get a helper not found error. Effectively the location narrows the type. |
text/0000-modifiers.md
Outdated
|
||
**May or May Not** | ||
- be called in the same tick as DOM insertion | ||
- have the the parent's children fully initialized in DOM |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall very positive to this RFC!
Minor thing: I guess "the parent's children" is the same as "the elements siblings"? If that's the case, would the term sibling be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd also like clarification. If children
is sibling elements, can't those belong to sibling components?
text/0000-modifiers.md
Outdated
MU paths: | ||
|
||
src/ui/components/flummux/element-modifier.js | ||
src/ui/routes/posts/-components/whipperwill/element-modifier.js |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like there's a discrepancy between the type name of element-modifier
and the base package + class names of @ember/modifier
+ Modifier
. Is there a reason to have the leading element
for one but not the other?
Relatedly, how does a dashed type name play with Rule 3 of the MU resolution rules? What would the identifier for a named element-modifier
export be?
I'm not sure what to think about that we increate the API surface for the application developer to make the API for the addon developer simpler. Not many people will ever write a component manager, but a lot of developers will use modifiers. And I think the story about Oh, and can I pass a modifier to a glimmer component and later use it with |
Some notes that I quickly come up with:
Although I see the benefits of modifiers (e.g. attaching listeners to elements wo/ js code). At the same time I see this critical as well as we will now declaratively set our listeners which is done programmatically in vanilla JS. With native classes Ember is catching up with recent javascript developments modifiers it looks like there is a chance we are splitting apart again? Components mentally always connect to Web Components (and shadow DOM in some way). Ember should more align with these concepts in order to onboard new ember devs. |
@luxferresum as mentioned before there are 2 implementations of To draw corollary, JS decorators have different implementations based on where the decorator is used. For instance a class decorator cannot be used as a accessor decorator unless the implementation allows for both. @gossi some answers to your questions
|
I do understand how |
text/0000-modifiers.md
Outdated
|
||
Glimmer components have `outerHTML` semantics, meaning what you see in the template is what you get in the DOM, there is no `tagName` that wraps the template. While this drastically simplifies the API for creating new components it makes reliable access to the a component's DOM structure very difficult to do. As [pointed out](https://github.com/emberjs/rfcs/pull/351#issuecomment-412123046) in the [Bounds RFC](https://github.com/emberjs/rfcs/pull/351) the stability of the nodes in the `Bounds` object creates way too many footguns. So a formalized way for accessing DOM in the Glimmer component world is still needed. | ||
|
||
Element modifiers allow for stable access of the DOM node they are installed on. This allows for programatic assess to DOM in Glimmer templates and also offers a more targeted construct for cases where classic components were being used. The introduction of this API will likely result in the proliferation of one or several popular addons for managing element event listeners, style and animation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/programatic assess/programmatic access/
?
This is an awesome RFC, exactly the kind of low-level primitive Ember needs to foster more experimentation. I think the power of abstractions that can be made with these sorts of things are under appreciated.
I think these modifiers could be used for so many useful things – animation for one. They are more like a horizontal composition/mixin pattern than a hierarchical/tree-like composition pattern (and many problem areas lend themselves to horizontal rather than vertical methods of reuse). Cannot wait to get my hands on these! |
@chadhietala Great examples! 🏅 We've built something similar to the performance marking example, but with components. Using modifiers would be much cleaner! And although I liked the preceding RFC, this one is easier to understand and more powerful/generic. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, I am excited to see this move forward again! Thanks for picking it up @chadhietala!
General questions I have after reviewing:
- This RFC doesn't specify how modifiers interact with angle bracket invocation. I think it should be specifically called out (either that we support them on angle invocations and what that means or that we don't).
- Based on the naming, I had assumed that
didUpdate
would be called both when incoming arguments have changed and when the underlying element was updated (e.g. an attribute value was updated/changed/etc). Did you consider that case and decide that we shouldn't invokedidUpdate
? - How could a modifier add attributes (e.g. classes,
style
,selected
, etc) before inserted into the DOM? It seems like the earliest hook we have isdidInsertElement
, but the guarantees there are that it has already been inserted. This would result in FOUC in some cases (imagine a<div {{style this.styles}}></div>
modifier) and broken behaviors in others (where specific attributes must be present when inserted into the DOM for them to function properly). Do we need awillInsertElement
(or possibly even just pass theelement
in to the constructor)?
text/0000-modifiers.md
Outdated
|
||
## Motivation | ||
|
||
Classic component instances have a `this.element` property which provides you a single DOM node as defined by `tagName`. The children of this node will be the DOM representation of what you wrote in your template. Templates are typically referred to having `innerHTML` semantics in classic components since there is a single wrapping element that is the parent of the template. These semantics allow for components to encapsulate some 3rd party JavaScript library or do some fine grain DOM manipulation. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe s/classic components/Ember.Component/ ?
text/0000-modifiers.md
Outdated
|
||
Classic component instances have a `this.element` property which provides you a single DOM node as defined by `tagName`. The children of this node will be the DOM representation of what you wrote in your template. Templates are typically referred to having `innerHTML` semantics in classic components since there is a single wrapping element that is the parent of the template. These semantics allow for components to encapsulate some 3rd party JavaScript library or do some fine grain DOM manipulation. | ||
|
||
Glimmer components have `outerHTML` semantics, meaning what you see in the template is what you get in the DOM, there is no `tagName` that wraps the template. While this drastically simplifies the API for creating new components it makes reliable access to the a component's DOM structure very difficult to do. As [pointed out](https://github.com/emberjs/rfcs/pull/351#issuecomment-412123046) in the [Bounds RFC](https://github.com/emberjs/rfcs/pull/351) the stability of the nodes in the `Bounds` object creates way too many footguns. So a formalized way for accessing DOM in the Glimmer component world is still needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/Glimmer components/custom components/ (we don't have "glimmer components" yet 😛, but "custom components" are included in 3.4.0).
text/0000-modifiers.md
Outdated
<b {{crum bing='whoop'}} zip="bango">Hm...</b> | ||
``` | ||
|
||
Element modifiers may be invoked with params or hash arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you mean they can accept positional and/or named arguments, right? As written this infers only positional or named are allowed at once (and that you can't use both).
text/0000-modifiers.md
Outdated
src/ui/routes/posts/-components/whipperwill/modifier.js | ||
``` | ||
|
||
In Module Unification, modifiers live within the generalized collection type "components" [as specified](https://github.com/dgeb/rfcs/blob/module-unification/text/0000-module-unification.md#components). Modifiers, like component and helpers, are eligible for local lookup. For example: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this link should probably not reference dgeb
's fork (I think the same link works on emberjs org too?)...
text/0000-modifiers.md
Outdated
|
||
#### `willDestroyElement` semantics | ||
|
||
`willDestroyElement` is called during the destruction of a template. It receives no arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The wording here seems odd. It doesn't seem that "destruction of a template" is actually the limit of willDestroyElement
. For example:
{{#if someCondition}}
<div {{flummux foo bar}}></div>
{{/if}}
When someCondition
change from true to false, I would expect flummux
's willDestroyElement
to be called but this is unrelated to "destruction of the template".
This hook has the following timing semantics: | ||
|
||
**Always** | ||
- called **after** all children modifier's `willDestroyElement` hook is called |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
||
## Alternatives | ||
|
||
The alternative to this is to create a "ref"-like API that is available in [other client side frameworks](https://reactjs.org/docs/refs-and-the-dom.html). This may look like the following: |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
@chadhietala I did some rereading and get a better understanding. Some of this is now more clear to me.
If that's correct, please add it somewhere at the top to give an immediate better understanding.
Questions
|
Motivated by #415 (comment) ( |
@buschtoens why would I do And how can |
@luxferresum element modifier managers RFC was merged and the feature implemented here: emberjs/ember.js#17143 Regarding Regarding |
@knownasilya But |
@luxferresum Ah, I don't think willInsert will be a thing, because you can handle that in the At least for now. It looks like https://github.com/emberjs/ember-render-modifiers is the way forward. |
Convert curly component to angle bracket component invocations see https://emberjs.github.io/rfcs/0311-angle-bracket-invocation.html the main advantage from this is that arguments and attributes are both first-class citizens now and components invoked with angle bracket syntax can use "modifiers" (see emberjs/rfcs#353)
Hey folks, what's the status on this? |
Based on the proposed implementation, and how everyone is using https://github.com/ember-modifier/ember-modifier, Though, the email about your comment got me thinking -- did Modifiers make it in without an RFC? |
<FooBar (draggable x=x y=y) /> | ||
``` | ||
|
||
This also means that they have no relationship to `...attributes`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand this, that means that a developer would not be able to forward the modifier. If so:
- What are the drawbacks of forwarding a modifier?
- What does that means for the
{{on}}
modifier? (currently can be forwarded)- Not giving modifiers the ability to forward and not changing
{{on}}
creates an inconsistent API - Changing
{{on}}
would be a breaking change
- Not giving modifiers the ability to forward and not changing
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is simply outdated information, as modifiers are "forwarded" in ...attributes
. The RFC for that was created and merged after this RFC was last updated: #435
Given that ember-modifiers has been implemented and has a nice API, is there anything blocking updating this text and merging it, with ember-modifiers as the official API? If so, what are the blockers? I.e. is there a path where we can ship a public modifier API for other addons to use, even if we end up needing to iterate on that design later? |
The modifier manager is not supporting modifying an element before it's insert into DOM. This limits some use cases, which should be supported. Please find more details about this in #652. The public API for functional modifiers exposed by |
@chriskrycho from my perspective, the biggest issue is unifying a bunch of features (modifiers, helpers, destroyables and ultimately usables) around a single high-level API. From the perspective of modifiers, the biggest inconsistency between what I hope we ultimately do and what ember-modifiers does is: https://github.com/ember-modifier/ember-modifier/blob/master/addon/-private/class/modifier-manager.ts#L37 In the absence of a modifier supplying updating hooks, I would like these APIs to default to teardown/recreate across the board, which would be a breaking change if we stabilized |
Per this open issue, the question of whether and how args should be passed to |
I think we discussed that using a proxy would allow lazy consumption of tracked tags (which solves the destroy issue). I volunteered to implement that behavior, but am waiting on some refactoring work to land from @pzuraq |
@lukemelia I totally agree with this. In general, destructors should be used in a way that's compatible with JS' new finalizer feature, which goes further than your request here and actually doesn't even make the object itself available to finalizers. This is because it's way to easy to accidentally resuscitate an object in its own finalizer, resulting in confusing bugs. Finalizers address the problem by allowing you to supply a different piece of metadata that gets passed into the destructor, so you can do parameter-specific things during destruction without waking the object back up. That was a long-winded way of saying I agree with your design proposal, and also think we should consider aligning RFC #580 (destroyables) with the finalizer design. @pzuraq Note that we would not want to use the finalizer design directly for our destroyables, because we want to offer more timing guarantees for resource cleanup. Our destroyables are closer to the explicit resource management JS proposal. (I mentioned the finalizer design because @lukemelia made me realize that the kinds of bugs that the current design enables are similar in spirit to the kinds of mistakes that the finalizer design is trying to avoid). |
Was this accidentally implemented? Do we still need this RFC |
I'm not sure if the RFC is this still needed. But I don't think it's implemented.
Maybe the RFC could be updated to add existing |
With #757 |
I'm not sure if I agree. That would make state-less, function-based modifiers a first-class concern with built-in support while state-full, class-based modifiers are user-land. If one is teached in the guides but the other not, that may make discoverability of the other option difficult. |
It's an improvement over today's current state of the discoverability of any modifiers difficult. but, I'd say most modifiers don't actually need state. State can be useful, but I think we'd want a separate RFC for figuring out class-based modifiers as default. |
Is this something we still need? We do have modifiers now. |
As mentioned in the description of the mentioned PR, #811 supersedes this, so we can close it! |
Rendered