Closed elcobvg closed 3 years ago
Is there any progress on this one? What's the status? :)
Agree with the proposal, it could be a usefull feature!
+1
Just closed #2080 in favor of this one, and a brief summary of the conversation there is:
slot
so that we can do <Foo slot='bar'/>
someday{#slot bar}<Foo/>{/slot}
, which would also allow a bunch of DOM nodes and components inside the slot, without them needing to be from a single component@pngwn raised a concern about syntax 2, which is that it would be an entirely different way of specifying slots, which I hear, and which I'm not sure what to do about.
Just closed #2080 in favor of this one, and a brief summary of the conversation there is:
- We already are reserving a prop called
slot
so that we can do<Foo slot='bar'/>
someday- Another possible syntax is
{#slot bar}<Foo/>{/slot}
, which would also allow a bunch of DOM nodes and components inside the slot, without them needing to be from a single component@pngwn raised a concern about syntax 2, which is that it would be an entirely different way of specifying slots, which I hear, and which I'm not sure what to do about.
If this is getting implemented, I think I'll love to see both implemented. I can see a lot of use cases where I would like to encapsulate the component with additional wrappers and in another scenarios I would like to just use the component. Now i work around this using empty div but then at times it breaks the structure because of the div element and I'll have to add more class utilities to make it work. This will be a great addition for Svelte.
I think this is very important feature to implement. Because for now we need to wrap target components by useless wrapper nodes. Anyway, thank you for awesome stuff, guys. Looking forward to.
+1
I would like this implemented. I don't like adding unnecessary divs.
I run into this on almost every project and end up doing this as a workaround:
:global([slot="content"])
This allows me to style that extra div in the component that contains the slots but it would be super nice to have <MyComponent slot="content"/>
and eliminate that extra div
The second proposed syntax is very useful for clarity. In vue, I often end up using quite a lot to separate actual props from the rest. Just an opinion of course.
+1
Bump
Hi, first time contributing to this project but I'm using Svelte (I love it!) for my own project and I bump up often against the limitation of not being able to pass arbitrary fragments to named slots without a wrapper element. So I've come to this thread a few times now.
The two syntaxes that seem to be preferred are namespaced-component syntax (<svelte:fragment slot="foo">...</svelte>
) and block syntax ({#slot "foo"}...{/slot}
).
The discussion around those syntaxes has been folded into this discussion about adding a slot
attribute to components.
To me the component syntax seems ideal.
If there's a slot attribute that works for elements and (eventually) components, when the desire to pass a component or multiple nodes into a named slot without a wrapper inevitably arises then this syntax seems like a natural extension.
This syntax easily provides all the features of components, like let:
bind:
and on:
. <svelte:fragment />
is just a component with a special name.
Again, since <svelte:fragment />
is basically just a shortcut for creating your own component with a naked default slot, this syntax seems easiest to implement.
Block syntax has a few drawbacks
Block syntax is only used for control structures so far. {#if ...}
, {#each ...}
, and {#await ...}
(the last one blurs the line a bit). Passing fragments to child components using a syntax that's otherwise reserved for a completely different kind of logical operation adds mental burden.
This syntax is not flexible and is not future proof. Directives and attributes/parameters can be given to elements and components. How would that work with this syntax?
And seems harder to implement.
slot
attribute<!-- Fragment.svelte -->
<slot />
If components gain the slot
attribute, then it would be possible to implement the proposed behavior of <svelte:fragment />
by creating a component that has a default slot with out any wrappers. However, I think it's still a good idea to add <svelte:fragment />
so everyone who encounters this common use case doesn't have to come up with their own slightly different solutions.
Another possible syntax is
{#slot bar}<Foo/>{/slot}
, which would also allow a bunch of DOM nodes and components inside the slot, without them needing to be from a single component
The original syntax allows for multiple DOM nodes to be used while still keeping with standards:
<Foo slot='bar'/>
<div slot='bar'>content</div>
<Bar slot='bar'/>
Another possible syntax is
{#slot bar}<Foo/>{/slot}
, which would also allow a bunch of DOM nodes and components inside the slot, without them needing to be from a single componentThe original syntax allows for multiple DOM nodes to be used while still keeping with standards:
<Foo slot='bar'/> <div slot='bar'>content</div> <Bar slot='bar'/>
As of yet this will not work inside blocks like {#each}
.
What is the state of this issue? Is there a workaround yet?
An alternative I've found is passing component references as props:
<script>
import Container from './Container.svelte';
import Child from './Child.svelte';
<script>
<Container child={Child} childProps={{message: 'Hellow'}}/>
And then on Container.svelte
:
<script>
export let child, childProps;
<script>
<svelte:component this={child} {...childProps}/>
It won't work in all use cases, but it's better than the div soup.
This is (still) one area where the VDOM approaches are so much better. The extra div soup is two fold:
A component reserving a slot but needing to perform manipulation on its content dom reference will need to add an extra element wrapper on the receiving site (some people mention it here: https://github.com/sveltejs/svelte/issues/2106).
A big app will have lots of components compared to regular html elements and these need to be wrapped before being fed to a slot, every single time on the call site (this issue)
A lot of classical components will hit both problems at once (e.g: a Dropdown that needs to measure its content to position itself properly and where the content is to be defined on the call site)
So we either have div soup (and lack of fun at reading it) or just make our components non configurable and copy paste all their code for each usage variation.
The above workaround is not acceptable, especially when using TypeScript; two props for a single thing is awkward and there's no way to prove the passed props type match the component's props at this point.
This is (still) one area where the VDOM approaches are so much better.
This is not an issue related to using a virtual DOM. Plenty of non VDOM libraries have solved this. Imba and Solid come to mind.
This is (still) one area where the VDOM approaches are so much better.
This is not an issue related to using a virtual DOM. Plenty of non VDOM libraries have solved this. Imba and Solid come to mind.
Absolutely, but it comes easily and naturally with the VDOM approach whereas it's a special case that must be coded here. It's just the frustration talking, after having taken this feature for granted.
This is (still) one area where the VDOM approaches are so much better.
but it comes easily and naturally with the VDOM approach
React didn't support rendering arrays without a wrapper for most of its existence
16.0.0 (September 26, 2017): Components can now return arrays and strings from render. (Docs coming soon!)
https://github.com/facebook/react/blob/master/CHANGELOG.md#1600-september-26-2017
Edit:
https://github.com/facebook/react/issues/2127
We know this is an issue and we know exactly what set of problem can be solved. We want this too but it is a hard problem with our current architecture. Additional comments expressing desire for this feature are not helpful. Feel free to subscribe to the issue (there's button in the right hand column) but do not comment unless you are adding value to the discussion. "Me too" and "+1" are not valuable, nor are use cases that have already been written in the comments (e.g., we know that you can't put
<tr>
or<dd>
elements with a<div>
).
emphasis mine
Sorry I'm new here, but is there a problem with just using another slot instead of a wrapper node workaround? I've used this in my own projects and it doesn't seem to have any issues with html/css generation or reactivity that I can tell.. I made an example here
Interesting 👀 . That feature (<slot slot="..."/>
) was only recently added in https://github.com/sveltejs/svelte/pull/4295. It wasn't primarily intended to be used that way, but I guess it's a good workaround for this issue.
I'm yet to find caveats to slotting components that way, other than it's inconvenient, as opposed to <Component slot="..."/>
.
<slot slot="">
seems good enough for the specifying side, it's probably better/more explicit than setting a fake attribute on a dom node/component anyway.
Just need a way to get at the slotted dom node from within a component now!
<slot slot="">
seems good enough for the specifying side, it's probably better/more explicit than setting a fake attribute on a dom node/component anyway.Just need a way to get at the slotted dom node from within a component now!
What do you mean "get at the slotted dom node"?
<slot slot="">
seems good enough for the specifying side, it's probably better/more explicit than setting a fake attribute on a dom node/component anyway. Just need a way to get at the slotted dom node from within a component now!What do you mean "get at the slotted dom node"?
From a component's script that has a
<slot slot="">
seems good enough for the specifying side, it's probably better/more explicit than setting a fake attribute on a dom node/component anyway. Just need a way to get at the slotted dom node from within a component now!What do you mean "get at the slotted dom node"?
From a component's script that has a , provide an easy way to read the element that was slotted in without any unnecessary wrapper.
I'm not sure I understand the point of what you're trying to do. Components inherently have no root node so there isn't just one "node slotted" that you could possibly reference. <slot slot="">
doesn't create a wrapper around your component in the DOM. It just injects your component as Svelte usually would.
If for some reason you did need access to a top level dom node for your component you'd have to wrap the component yourself and then use something like bind:this={self}
and access the DOM node in onMount
.
Forgive me if I'm misunderstanding you here.
Just access the slotted node, whether it's the default one or a named one.
Right now, we can only know if a slot was provided (I think the API is a Record<slotName, boolean> but we can't access the DOM references.
Sorry I'm new here, but is there a problem with just using another slot instead of a wrapper node workaround? I've used this in my own projects and it doesn't seem to have any issues with html/css generation or reactivity that I can tell.. I made an example here
I don't think this is intended to work 😅 But good to know, until #4556 will be merged eventually.
Just access the slotted node, whether it's the default one or a named one.
Right now, we can only know if a slot was provided (I think the API is a Record<slotName, boolean> but we can't access the DOM references.
Grabbing the DOM references would be challenging. E.g. the following may or may not have elements and may have multiple nodes when it does depending on the contents of AnotherComponent
.
<Component>
{#if somevalue}
<div>my slotted info</div>
<AnotherComponent/>
{/if}
</Component>
If Component
puts the slot inside an #each
, that additionally complicates the issue.
We have let
for passing data into the slot. Perhaps we could have something to pass data back to the parent?
<Component let:value give:node={myNode}>
{#if value}
<div bind:instance={myNode}>my slotted info</div>
<AnotherComponent/>
{/if}
</Component>
You couldn't use bind:this
since that would bind it in the scope of the containing component, but maybe a special bind:instance
for this case could be used.
It seems like a lot of added API in the framework would be needed to meet the desire to access slot contents.
I also want to add to @jacwright 's comment to say that if you're trying to access DOM elements like this, it's hightly likely that you're using Svelte in a way which isn't a good fit for it, and your mental model of how a Svelte application works is incorrect. Svelte is a reactive UI language. It allows you to easily create things which react to state changes automatically.
In Svelte, you manipulate a model of data, and Svelte handles the required DOM manipulation to reflect that state. If you edit the DOM, you are doing Svelte's job.
If you want to modify a DOM element, you should do so by updating the state. You can do this for slotted content by declaring a context at the topmost level of your subtree, and changing elements within that context. Your slotted content can then read that content and react to changes which happen there.
Often when an API appears "missing" from Svelte, it's actually instead absent, and often for a very good reason.
I also want to add to @jacwright 's comment to say that if you're trying to access DOM elements like this, it's hightly likely that you're using Svelte in a way which isn't a good fit for it, and your mental model of how a Svelte application works is incorrect. Svelte is a reactive UI language. It allows you to easily create things which react to state changes automatically.
In Svelte, you manipulate a model of data, and Svelte handles the required DOM manipulation to reflect that state. If you edit the DOM, you are doing Svelte's job.
If you want to modify a DOM element, you should do so by updating the state. You can do this for slotted content by declaring a context at the topmost level of your subtree, and changing elements within that context. Your slotted content can then read that content and react to changes which happen there.
Often when an API appears "missing" from Svelte, it's actually instead absent, and often for a very good reason.
While the previous comment was spot on, your point is a harder sell. Getting a DOM ref is pretty standard and svelte has an API for it (bind:this). I'm only saying it should ideally be available for slotted elements as well.
While the previous comment was spot on, your point is a harder sell. Getting a DOM ref is pretty standard and svelte has an API for it (bind:this). I'm only saying it should ideally be available for slotted elements as well.
It's there because it's possible at compile time since the presence of it is guaranteed. Since slotted content is not guaranteed, it would be a runtime concern.
While the previous comment was spot on, your point is a harder sell. Getting a DOM ref is pretty standard and svelte has an API for it (bind:this). I'm only saying it should ideally be available for slotted elements as well.
It's there because it's possible at compile time since the presence of it is guaranteed. Since slotted content is not guaranteed, it would be a runtime concern.
No, it's not any more guaranteed than a slot's content.
https://svelte.dev/repl/07012a40613f49709479088be103681a?version=3.30.1
Let's not pretend it's missing for good practices reasons. Convoluted api? sure, maybe!
No, it's not any more guaranteed than a slot's content.
https://svelte.dev/repl/07012a40613f49709479088be103681a?version=3.30.1
Let's not pretend it's missing for good practices reasons. Convoluted api? sure, maybe!
I'm not really enjoying your tone. An if
statement which is either true or false based on a condition is very different from slotted content where the contents can be completely calculated based on a runtime condition such as data being fetched or an async import.
Just access the slotted node, whether it's the default one or a named one. Right now, we can only know if a slot was provided (I think the API is a Record<slotName, boolean> but we can't access the DOM references.
Grabbing the DOM references would be challenging. E.g. the following may or may not have elements and may have multiple nodes when it does depending on the contents of
AnotherComponent
.<Component> {#if somevalue} <div>my slotted info</div> <AnotherComponent/> {/if} </Component>
If
Component
puts the slot inside an#each
, that additionally complicates the issue.We have
let
for passing data into the slot. Perhaps we could have something to pass data back to the parent?<Component let:value give:node={myNode}> {#if value} <div bind:instance={myNode}>my slotted info</div> <AnotherComponent/> {/if} </Component>
You couldn't use
bind:this
since that would bind it in the scope of the containing component, but maybe a specialbind:instance
for this case could be used.It seems like a lot of added API in the framework would be needed to meet the desire to access slot contents.
You can do this already, kinda https://svelte.dev/repl/69d03236b3174957adb7434ba47eaf39?version=3.30.1
No, it's not any more guaranteed than a slot's content. https://svelte.dev/repl/07012a40613f49709479088be103681a?version=3.30.1 Let's not pretend it's missing for good practices reasons. Convoluted api? sure, maybe!
I'm not really enjoying your tone. An
if
statement which is either true or false based on a condition is very different from slotted content where the contents can be completely calculated based on a runtime condition such as data being fetched or an async import.
Nor do I like yours (you started insinuating that if I need this feature it's because I don't use svelte properly which is very ridiculous) but I will leave it at that; it's fine if you don't get it as far as I'm concerned. Please stop replying to me.
<Component> {#if somevalue} <div>my slotted info</div> <AnotherComponent/> {/if}
You can do this already, kinda https://svelte.dev/repl/69d03236b3174957adb7434ba47eaf39?version=3.30.1
Ohh, didn't know register
, that's good enough for the occasional cases when we need it. Is that API documented? It looks a bit like actions but not quite.
Edit: Ahh ok, it's a regular let. It's too bad it has to be so intrusive on the call sites.
I think the best we have right now is go from a div-soup to a well-named-elements-soup :D (https://github.com/sveltejs/svelte-virtual-list/blob/master/VirtualList.svelte#L165)
@AlexGalays register
is an action created and passed in from the parent node (Wrapper) which allows the child to register with it. Not builtin to svelte.
That's very clever @PatrickG. Nice one. I was a bit confused when first looking at it to understand what was going on, but I think that will be a handy tool in the toolbox.
It would be nice if instead of only html elements, we could put components in named slots as well. Like this:
<MyComponent slot="title">Title here</MyComponent>