Closed KenAJoh closed 1 year ago
To clarify: The issue is that you want to create web components that should be used together in a way that these components know of their children so they can put them into specific slots without you having to explicitly define those slots as a user?
A potential solution would as a side-effect allow this to be done i guess. My main issue is how to access and manipulate state of child web-components
// html
<wc-accordion>
<wc-accordion-item>
...
</wc-accordion-item>
</wc-accordion>
// svelte code (wc-accordion)
<script>
let ref: HTMLDivElement;
onMount(() => {
ref.querySelector("wc-accordion-item") // <- null
ref.parentElement // <- null
ref.shadowRoot // <- null
});
</script>
<div bind:this={ref}>
<slot />
</div>
// dom
<wc-accordion>
#shadow-root (open)
<div></div> <- Can only access this
<wc-accordion-item> <- and not this since its outside shadow-root
...
</wc-accordion-item>
</wc-accordion>
Making it hard to pass state down to all children like this
<wc-accordion variant="neutral">
--
const item = ref.querySelector("wc-accordion-item");
item.variant = "neutral"
while it makes sense that you cant access elements outside the shadow-root with querySelector
directly on the element, having access to either parentElement
or shadowRoot
would potentially open up an easier way to do this.
Use getRootNode and host
, then you can query the other elements: ref.getRootNode().host.querySelector('wc-accordion-item')
.
Given that this is now possible using public APIs (the previous version with get_current_component
is merely a hack using internal APIs which is brittle) I'm marking this as a docs issue - not sure where in the new docs to put it yet.
Thanks for the help! ref.getRootNode().host
did the trick for us āļø Getting som ts-errors with host: Property 'host' does not exist on type 'Node'.
, but closing this issue as completed as it still works.
The easiest way i have found for now is to make queries on the document, but this adds a different complexity with checking for the correct dom-element and adding unique identifiers to each component instance.
I concur; ideally you would just stay entirely in Svelte to intuitively get/set context and could just compose your custom elements any way you want. The goal would be for context to "just work" in the child element, as long as it is nested somehow under the parent element.
Like... this: https://svelte.dev/repl/b404a1addaf348eabcff3f6089707297?version=4.2.1
The advantage of using svelte-retag
in this way is that you also have plain Svelte components as well... like this: https://svelte.dev/repl/7a26be30148644b6895469274bb69a32?version=4.2.1
To me this is a pretty useful pattern! Check out svelte-retag
if you want to try this approach to get context working cleanly in custom elements. Also, check out this tab-based demo here too: https://svelte-retag.vercel.app/
Edit: p.s. As you can see in the first demo above, you can also incorporate the custom elements directly into Svelte components too (note the <custom-parent>
included in the App.svelte
file); this is a native feature of Svelte anyway. So... you can still mix and match it as you see fit, which I also think is pretty cool.
Describe the problem
The new changes made to custom-elements š changed how
get_current_component
works, and with it comes some changes to how one create a component-api with purely web-components. (as far as my current testing has lead me to believe, but might be mistaken here)A constant hassle when working with shadow dom is how one passes values between the shadow-dom, and in v3 this could be "solved" by using
get_current_component().shadowRoot
to access both parent and childrens within slots. This allowed the following component-pattern:where
accordion-item
stored and passed both the "open"-state and the "ontoggle"-handler down toaccordion-heading
andaccordion-content
. This "worked", but was not pain-free...One could sort-of solve this now by using the following pattern:
But that doesn't solve the core issue...
One could dispatch events from
accordion-header
up to item, following the 'properties down, events up'-pattern, but what is the expected and preferred way to pass theopen
-prop down across the nested shadow-dom? This closed issue adds context-support for web-components, possibly solving this. But so far with 4.0.0-next.0 i have yet to get this working (can set context, but getContext is alwaysundefined
)What i'm getting to is: How does Sveltes custom-elements want and expect a good component-pattern to look and work with 2-way communication between components?
Describe the proposed solution
Stencil solves this by making it possible to get and change child web-components with
querySelector
directly on the parent element even its if its nested shadow-domsHow this is implemented i'm not aware of, and might only be possible with virtual-dom (as stencil uses)
Lit solves this with giving access to
this.children
orthis.defaultNodes
(not 100% ondefaultNodes
, but thats how spectrum web-components does it)Alternatives considered
In v4 one could disable shadow-dom for the nested elements, and only use shadow-dom on the wrapper. This could a viable pattern, but have yet to do any testing with it for now.accordion-item
will still be outside the wrappers shadow-dom, so not viable.The easiest way i have found for now is to make queries on the document, but this adds a different complexity with checking for the correct dom-element and adding unique identifiers to each component instance.
This issue under the 4.x milestone is related and would solve this to a certain degree. Found a PR on this, but its 3y old now..
Importance
would make my life easier