-
-
Notifications
You must be signed in to change notification settings - Fork 404
Component Manager Bounds #351
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
Conversation
rwjblue
left a comment
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, thank you for putting this together @chadhietala!
Some things:
- Can you update the
boundssection to make it clear that theboundsobject itself is stable? - Can you show a few examples of component templates, and show what
boundswould be for them?
|
|
||
| In [RFC#2013](https://github.com/emberjs/rfcs/blob/master/text/0213-custom-components.md#detailed-design) we outlined the component lifecycle and how a custom component is associated with a manager. This design builds off off of that existing work and concepts. | ||
|
|
||
| ### `elementHook` capability |
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.
elementHook doesn't really seem to fit with the other capability names. Suggestions:
elementAccessdomAccessdomAccessCallbacksdomCallbackselementCallbacksbounds(I like this one best 😄)
Thoughts?
| }); | ||
| ``` | ||
|
|
||
| ### `didRenderLayout` hook |
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 know that this is the name internally in the glimmer-vm, but I don't think its particularly relevant as a name.
Possibly: didCreateBounds?
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.
Actually, can we avoid creating a new hook here and instead add bounds as a second argument to the already existing didCreateComponent hook? didCreateComponent is part of the asyncLifecycleCallbacks capability, and per the prior RFC is where didInsertElement should go.
The name seems much better, and the intent seems very much in line with what we are doing here...
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 don't mind bike shedding about the name, but I don't think we want to bundle it with didCreateComponent. There may be situations that you need one but not the other, and we want to keep things as granular as possible. Plus, bundling the hooks forces their timing to be the same, which we probably don't want (and they are in fact different today).
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.
Sounds good, I agree. Still would prefer another name than didRenderLayout it really doesn't feel cohesive with the other component manager method names.
|
|
||
| didRenderLayout(component, bounds) { | ||
| component.bounds = bounds; | ||
| component.didInsertElement(); |
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.
Based on the prior RFC didInsertElement is more appropriately called in didCreateComponent (which was an optional asyncLifecycleCallbacks capability), I don't think we should recommend changing that...
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.
In general, I think we need to recommend against calling any kind of user hooks here.
| ``` | ||
|
|
||
| ### `didRenderLayout` hook | ||
| Once the rendering engine has constructed the bounds for the component the `didRenderLayout` hook will be called with the `component` and a `Bounds` instance. |
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 phrasing is slightly off, the hook does not receive "the component" it receives the opaque result of calling createComponent (aka the "state bucket"). Different component managers may return the instance itself but it may also return some other POJO/state bucket.
|
|
||
| In other words, components **should not** rely on the timing of DOM insertion outside of the component's own bounds. | ||
|
|
||
| ### `willDestroyLayout` hook |
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.
Assuming we can keep the bounds as a second arg to createComponent (one of my suggestions above), this can become willDestroyComponent...
|
|
||
| ### `willDestroyLayout` hook | ||
|
|
||
| To balance `didRenderLayout` we will expose a hook called `willDestroyLayout` that is invoked during destruction. It will receive the `component` instance. |
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 doesn't get the component instance, it gets the "return value from createComponent" (may be a state bucket, may be a component instance)
| type FirstNode = Node; | ||
| type LastNode = Node; | ||
|
|
||
| interface Bounds { |
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.
Bounds doesn't get .parentElement?
The interface in glimmer-vm does have parentElement property:
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.
Due to the lack of timing guarantees of construction of the parent do think this actually useful?
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 we need to provide access to the parent. What we cannot guarantee is that the parent's children (i.e. your siblings) are fully constructed. (And possibly that the grandparent may not be construed yet.)
By the way, I think you meant to say:
interface Bounds {
firstNode: Node | Bounds;
lastNode: Node | Bounds;
}|
I think I incorporated the feedback. |
rwjblue
left a comment
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'm 👍 here...
|
Has anyone proposed a react-style “refs” api? Instead of relying on the framework to create special properties, we could have a declarative api that allows decorating the template with special attributes that create JavaScript-accessible node references. Something like I’m just curious. Both APIs could exist simultaneously. |
|
Not frequently, but I've had the need for this in more complex components and the workarounds aren't pretty or easy to test. 👍 |
|
We discovered a problem with this design in the core team meeting last week. The intent of this is to offer functionality akin to classic component's On the surface, it seems like this gets the job done. However, consider the following component template: {{#if this.isSelected}}
<strong>selected</strong>
{{else}}
<em>select me</em>
{{/if}}Ignoring the whitespace nodes, the bounds object will point to the Of course, in a sense this is no different than attaching event listeners directly to descendants of There is still a class of use cases that is somewhat compelling and does not have the problem. For example, a component may use the bounds to "autofocus" the first input element. This only involve accessing the bounds' content synchronously and does not require any cleanup. In general, these kind of "snapshotting" use cases are fine. However, they may call for more hooks, like one that runs after the bounds' content has (potentially) been updated so they could reapply the logic. So we can adapt this proposal for that kind of "snapshotting" use cases, perhaps adding the update hook and possibly removing the teardown hook (cause it is not needed, and may be misleading). We can then explore alternatives for the original use case, perhaps a modifier/ref-based design or an additional capability that ensures (at runtime) that the component has a stable root element. However, my worry is that since the original use case is the most common and compelling one, having this API around without a solution for that use case will mislead our users and causing them to misuse this API. |
|
Relevant: emberjs/ember.js#12500 |
|
Closing in favor of #353 |
Rendered