4 ms·
> The real power of HTMX and Web Components is that Web Components self-initialize when they get attached to the DOM, I've run into some problems with the Cust
by lelanthran 3y ago
> The real power of HTMX and Web Components is that Web Components self-initialize when they get attached to the DOM,
I've run into some problems with the Custom Element lifecycle, though. Not sure how to solve them, because `connectedCallback` is the wrong place for my custom code. The odds are I am doing it wrong (or have a fundamental misunderstanding!)
Basically, lets say I have two custom elements: A and B.
All Bs are a descendant (even if not direct) of an A.
Each A grabs all B children and copies the data out of a specified child of the B, then uses it.[1]
When the connectedCallback for A is executed, the Bs are not yet connected and thus don't show up in a call to querySelector.
I've (sort of) worked around this by having each B, when connected, find the closest A ancestor, and add itself to an array field of that ancestor. A's connectedCallback performs a setInterval with ever-increasing delays until some specific maximum (I use 10ms). The setInterval just redisplays with whatever labels are in the array.
Now, this is probably a stupid way of doing this, and I believe a better way is to use MutationObserver, which I will probably switch to at some point.
However, my point is still: the life-cycle did not offer me some way to hook into A only when all the children are attached to the DOM.
[1] A practical example of this would be tabbed-containers. The <tab-container> shouldn't hold the labels for each <tab-item>, it should get the labels from the <tab-item> itself.
- have_faith 3y agoI think your workaround is probably the most preferable of the options. I've worked with mutation observers a lot and I wouldn't want to rely on them for anything that didn't have a simpler mechanism to achieve the same thing. In "regular" frameworks, this problem would be expressed as state that is owned by A, that B's contribute to. Maybe by calling a function passed down to them, or by updating a shared store. It's a big shortcoming that composition of web components is limited to string attributes unless you use some sort of framework to mediate things.
- AndrewHampton 3y agoAnother option would be for the B to dispatch a `CustomEvent` on itself. That event will bubble up the DOM until it hits A. A would then need an event listener that would probably stop propagation and do whatever bookkeeping is necessary.
- tomhallett 3y agoThis seems like the best option, because it works just as well if the child components are loaded async (Ie: a tab component loading sub tabs on hover, and then the parent tab container needing to subscribe to that)
- DrScientist 3y agoI believe this is what whenDefined was made for. https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/whenDefined https://developer.mozilla.org/en-US/docs/Web/API/CustomEleme... It's not been well covered in the standard Custom components blogs.
- papa0101 3y agoslotchange is your answer. In the connectedCallback add the eventListener and inside your slotchange handler: const myBs = this._slot?.assignedElements();
- Inviz 3y agoI think this is decent approach, although setInterval may be unnecessary. I do this similarily as a base Class, and i made a generic solution that would not allow for A's to be initialized until all of its children registered thsemselves.