-
-
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
Expose a parentElement property for (tagless) components #168
Comments
As you discovered, there is private API in Ember 2.9+ that allows access to a components bounds (including parentElement). This support was added for glimmer2 support in Ember Inspector. I think we would definitely consider making that method ( |
This also seems like something where element modifiers might play in as well. |
@rwjblue would be fine for me as well! @nathanhammond to be honest I don't see the connection here. Could you elaborate on it? |
There's also the "eternal" emberjs/ember.js#12500 |
@miguelcobain Thanks for pointing this out! Yeah, if that PR would land, it would solve this issue as well. Maybe It's time to "revive" your PR when Glimmer2 has finally landed in 2.9? :) |
As it has been quiet on this one for a while, how can we proceed? Is making |
Ya, an RFC for that seems like a good next step. |
Ok, great, will go for it. My motivation for this came from the specific use case of getting the parent from a tagless component. But |
@simonihmig is there an official RFC for this? In the meanwhile, @pzuraq came up with a clever workaround for ember-popper where we get the parent node of an |
Oh yeah, this would be much better than my hack. Would definitely love to see this become an RFC 😄 |
@simonihmig now that modifiers are a thing, I think this can be closed right? |
Well, 2.5y later I wasn't even aware this was still open! 😝 And yes, now with Glimmer components the original request to extend But still not sure how we could implement the use case properly with modifiers or whatever other public API we have now. Given the original example:
FWIW, it's still using that empty TextNode hack... |
To move this to a modifier, what about instead of using
Which provides access to the |
A quirky workaround that I think should get the job done, and which doesn't require any hackery or use of services, though it isn't necessarily great: import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { guidFor } from '@ember/object';
export default class WillHaveTooltip extends Component {
@tracked id;
constructor() {
super(...arguments);
this.id = guidFor(this);
}
} <button id={{this.id}}>
Create
<BsTooltip @title="Just create something" @parentId={{this.id}}/>
</button> Then const targetEl = document.querySelector(`#${this.args.parentId}`);
assert(targetEl instanceof HTMLElement);
// off to the races This is, to be clear, less elegant than we might like – in particular, it would be nice if we could do all of that nicely in a template-only component – but it should work. |
@Panman82 Thanks for the suggestion, which probably would work. But given that the motivation for bringing this topic up is to have a public and easy to use API for that use case, the amount of orchestration needed in your approach does not really qualify as easy 😉 Also it wouldn't work for
Yeah, that's possible, and indeed is already supported as an alternative way to specify the tooltip's |
@simonihmig - TBH, I got confused a bit when I read through this (partially because of its age, and the community thinking has changed quite a bit since you original posted the issue). Can you help me understand why you would want to know the caller's parent element? Also, RE: |
Basically what I wrote in my initial posting:
So having a way to support this API, that attaches a tooltip to its parent element: <button>
Create
<BsTooltip @title="Just create something"/>
</button> With a classic Ember component, this would be possible using |
Hmmm, when do you need to know your parent DOM element? Only after you do want to render something, or eagerly in order to set things up for later? |
Eagerly, as I want to attach event listeners to the parent element. |
I think a modifier is more suitable for this use case, at least I've created one to do the exact thing in our project. Are there any possibilities that a modifier can't access the parent DOM element? |
Yeah, agree. I think providing both the modifier and the component to do the rendering seems good/fine to me for this case. 🤔 |
Ok, but how would you "connect" the modifier (which attaches an event listener) to the component (which should render stuff when the event occurs)? If we had contextual modifiers, we could do something like this I guess: <BsTooltip @title="Just create something" as |attach|>
<button {{attach}}>Create</button>
<BsTooltip> But that's no a thing for now. Again to be clear, it's not that there are no ways to do this, it's just that I would want to make this as simple as possible, and not offload any kind of orchestration work to the user for such a trivial thing as adding a tooltip. Just invoking a tooltip component would satisfy that constraint, but again requires some way to access the parent element. Given that this issue stems from a time of classic APIs like |
Below is how we did to implement a modifier based https://ember-twiddle.com/bb00a66732941c05d82be32f0046cdc6 @simonihmig I understand that if there's a reference of the parent element, things could be a little easier, at least in the demo above, I could remove the line of In this particular example, a @rwjblue A tagless component may render nothing in place and send over all of its content to any other places in the DOM tree instead. I believe this is the reason that @simonihmig requires a reference of the parent element here. |
@simonihmig FWIW, I am not really trying to suggest that we close this. I'm mostly trying to properly understand the problem space. I just haven't hit this type of issue / usage in the past. |
@nightire sure, that makes sense, but these exact scenarios have gotten us into massive trouble in the past (look at the whole slew of |
@simonihmig the snippet you pasted actually seems pretty nice: <BsTooltip @title="Just create something" as |attach|>
<button {{attach}}>Create</button>
<BsTooltip> The contextual modifiers RFC already landed in emberjs/rfcs#432, and a bit of work towards implementing the plumbing has been done and absorbed in the rendering engine updates from Ember 3.17+. |
Wow, I don't even know there's an RFC.🤣 |
@nightire thanks for that example, makes things clear now! As an addon author I would be worried though to (temporarily) render that
Well, yes and no IMHO. In terms of the use of Ember's new and modern primitives (modifiers, contextual modifiers) maybe, but the initial snippet still seems preferable to me. This one suggest that the button is a child of a Tooltip, which is not the case. And it's less readable I think, as the button is actually the important and meaningful part in the template, the tooltip is secondary.
Yeah, I know, it was more myself who suggested that! 😉 As that empty text node trick is "good enough" for that rare use case, and probably does not justify increasing the public API surface therefore. Especially as an API for this does not really fit that well with the modern Ember world anymore (not even a |
With rehydration the FastBoot rendered empty TextNode (used to find the component's parent element) is reused. So the TextNode instance that the component has created at runtime is not attached to the DOM, thus cannot be used to find the parent. In this case Ember's private API `Ember.ViewUtils.getViewBounds()` is used as a fallback. Related: emberjs/rfcs#168
This wouldn't support using <button>
...
<BsTooltip>
Some content for the tooltip.
</BsTooltip>
</button> I think it's easier to read in block mode than with <button>
...
<BsTooltip>
<em>Tooltip</em> <u>with</u> <b>HTML</b>
</BsTooltip>
</button> This is explicitly supported by Bootstrap. I think there needs to be a replacement available before |
Given that the proposed alternatives all have more or less substantial drawbacks, and that empty TextNode trick does not work with FastBoot rehydration, I am thinking of finally writing up an RFC for this. @rwjblue you approved that a long time ago, would you still think in the current Octane era that an RFC to make |
In your RFC, could we explore a different name for this @simonihmig ? just makes me think more of "getBoundingClientRect" then anything. |
Haven't given this much thought yet, but sure! Also I think we would need to provide a proper module export, rather than accessing it from the |
I'm also encountering similar issues here when authoring a popover Glimmer component in Octane. Being able to reference the parent element from an I can pass a
An API to allow |
@Skwai this is how I do popovers with button elements: tldr:
api is this: <PopperJS as |reference popover|>
<button {{reference}} {{on "click" this.yourClickHandler}}>
{{yield to="trigger"}}
</button>
{{#if this.yourVisibilityIndicator}}
<div {{popover}}>
This is a popover!
{{yield to="default"}}
</div>
{{/if}}
</PopperJS> modifiers can be passed willy nilly as args, so you could thread through to whatever component you need to |
@simonihmig is there still an intent to write an RFC? How can we help facilitate that? |
Hm, not really. I have some things in mind to write an RFC for, which feel more important (to me). Currently we have two options at hand, which seem "good enough":
So I'll close this for now, as this narrow use case (for which there are ok-ish solutions) probably does not fully justify a new public API. |
Components have an
element
property that gives you the DOM element of the component itself. Getting its parent is pretty easy, usingelement.parentElement
orthis.$().parent()
. But these approaches fail when dealing with tagless components, which have noelement
and nothis.$()
either. Obviously, they still have a parent though!So far I have used this utility function:
This seems to work for all Ember versions (pre and post Glimmer2), but is obviously pretty hacky as it is using private APIs. And there does not seem to be any way to get the parent element for a tagless component using just public APIs, unless I am overlooking something!?
So my suggestion would be to introduce a
parentElement
property, in analogy to theelement
property.An example use case: I want to make a tooltip component, that should be used like this:
The tooltip component must have access to its parent element, to attach event listeners to it, but should not (until the tooltip is actually shown) add anything to the DOM. Thus a tagless component.
Any thoughts?
The text was updated successfully, but these errors were encountered: