-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Feature: Let slots be wrapped in if statements to avoid "must be a child of a component" error. #5604
Comments
I would enjoy this feature. Is there a workaround for now, to have conditional slots? (Except of copy paste..) |
+1. this would help make my code less verbose in some cases. |
+1, Especially useful for slot forwarding!!! https://svelte.dev/repl/7941b94f6c6f42df93aba4d5ef543917?version=3.38.2 |
I agree, this is a must-have for complex components. Code duplication is one of the largest sources of bugs, please don't make us do it. |
Here's the workaround I'm using: {#if $$slots.header}
<header>
<slot name="header" />
</header>
{/if}
<style>
header {
padding: 16px;
/* ... */
}
header:empty {
display: none;
}
</style> This means that when you have something like this: <Demo>
<svelte:fragment slot="header">
{#if condition}
<p>HELLO</p>
{/if}
</svelte:fragment>
</Demo> You will only see the |
We just ran into the same thing -- trying to conditionally pass in a slot. Being able to do this makes sense.... In our case, we are trying to conditionally render a slot named "body" within an expandableCard component. If there's no body slot given to expandableCard, just render the header without expander body. I guess our workaround would be to pass in a Boolean prop to expandableCard called "hasBody", and then conditionally render the expander div that has the |
Hey. It'd be great to get this as a feature. Even with |
Issues like this one, the lack of a |
Appreciate the CSS tip @lukeed. For anyone using tailwindcss you can do this with the following: <header class="p-4 empty:p-0"> |
Well, I currently use this ugly workaround with CSS display instead of if:
(use display depending on your element type, e.g. table-row for table rows etc.) |
Also just ran into this, assumed it was a bug rather than a missing feature because it seems like a no brainer. Is there any input from the svelte team on this? Is it due to technical limitations of the compiler? Or do we need an RFC for it? Would love to move it forward. |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This will be really helpful when creating custom component with predefined options and pass fragments to child component. For example: |
This comment was marked as off-topic.
This comment was marked as off-topic.
I think have a workaround. I created a Svelte component that I named SlotFragment.svelte. It conditionally renders a default slot. {#if $$slots.default}
<slot />
{/if} For example when composing components, I have component A with a I have component B that that renders component A plus some other stuff. Component B provides a This syntax in component B works, but causes component A to always think the slot is filled and it renders the additional HTML. <slot name="header" slot="header" /> So you try to put an #if around it, but the slot must be filled within a component. {#if $$slots.header}
<slot name="header" slot="header" />
{/if} Then you try to wrap it in a svelte:fragment, but it can't be in the #if either: {#if $$slots.header}
<svelte:fragment>
<slot name="header" slot="header" />
<svelte:fragment>
{/if} However, using the SlotFragment component works: {#if $$slots.header}
<SlotFragment slot="header">
<slot name="header" />
<SlotFragment>
{/if} |
Also ran into this issue. Conditional slots would be a very useful feature |
I ran into this issue yesterday and spun up a repl to reproduce - https://svelte.dev/repl/b089c2c379e9404596445c16311bd1b9?version=3.50.1. |
This comment was marked as off-topic.
This comment was marked as off-topic.
Please add this feature, it is deeply important for us to fully use the slot concept. I had to waive the slot feature and fully integrate the child component into the parent one, because of this, which goes against the concept of extracting logic into components. Now I have to duplicate code in case I would need a similar child component in the future. Which unfortunately is bad practice. In my case the if and each tag are generating the error. Thanks |
This limitation is particularly annoying when using transitions, since the simple workaround is to hide the element when the slot shouldn't be rendered, rather than removing it from the DOM entirely. Could a |
At the very least add an error message or something. I have just been debugging for two hours questioning everything I knew about Svelte bindings because I was trying to conditionally insert a component into a slot. For people who are in a similar situation. You can wrap you component in a div and assign the slot there.
This does:
As you'll probably reckon it is a band-aid solution because empty divs are far from ideal. |
@uranderu thanks for that tip, I just came across this issue when trying to do the same. So far I've been super impressed with Svelte since its amazing, this is the first trouble I run into tbh.. As a work around for making Fallbacks work I added an else to pass In the EM , But id rather have the em in the child comp tbh.. <div slot="content">
{#if post.content !== undefined}
<textarea>{post.content}</textarea>
{:else}
<em>No post content was found</em>
{/if}
</div>
|
For those who are running into this issue and require the element to actually be removed from the DOM rather than just hidden with CSS or wrapped in a slotted parent element, I came up with this action as a workaround. function hideOrRemove({
node,
parent,
nextSibling,
shouldHide,
}: {
node: HTMLElement;
parent: HTMLElement | null;
nextSibling: ChildNode | null;
shouldHide: boolean;
}) {
if (shouldHide) {
node.remove();
return;
}
if (parent?.contains(nextSibling)) {
parent.insertBefore(node, nextSibling);
return;
}
parent?.appendChild(node);
}
export function hideElement(node: HTMLElement, condition: boolean) {
const nodeParent = node.parentElement;
const nodeNextSibling = node.nextSibling;
hideOrRemove({
node,
parent: nodeParent,
nextSibling: nodeNextSibling,
shouldHide: condition,
});
return {
update(newCondition: boolean) {
hideOrRemove({
node,
parent: nodeParent,
nextSibling: nodeNextSibling,
shouldHide: newCondition,
});
},
};
} Which you can then use like so: <div slot="header" use:hideElement={$shouldShowHeader} /> I would caution to consider this a dangerous approach and to use this at your own risk, as I've only used this for one specific use case, and have not given it much testing or investigation into how it might interact with svelte internals. |
New CommandPal option footerText. If set to null (default) no footer is displayed. If set to a string the string is displayed. The way I handle conditionally showing the footer is kind of ugly. Svelte doesn't have conditional slots. Once: sveltejs/svelte#5604 is implemented, the whole mess can be cleanly rewritten. I would have like to use :where(.footer) in the css for styling. But there are a lot of mobile browsers on caniuse that have unknown status even 2 years after their release. Using :where would allow ".footer" in user's css to work. As it is you have to make ".footer" more specific using e.g. "#id .footer" or ".footer[slot=footer]". README.md, and cp-advanced files updated to show/use.
This requires ugly hacks to work around when using slot forwarding to child components. Its a greatly needed feature. |
A feature with this level of importance should have given more attention than this IMHO. |
While this issue still exists, I do have a workaround to propose. Conditionnal slots work fine with default slots, but not with named ones. What I do is just pass an extra prop to the components declaring the named slot that will enable/disable its display from within. It's not as elegant, but it works fine. Here's a REPL representing the use case : https://svelte.dev/repl/dda911c0804c43bd8d6d035ed0660e22?version=3.58.0 |
This feature is required. My use case is: My current solution to add extra prop with visibility flags, and show fallback if flag is true, and slot if flag is false. It is not a declarative way to solve this problem, sadly. |
For anyone having a hard time following up here: This may be fixed by #8304 as mentioned above but as Svelte 5 changes a lot of stuff, they are waiting with merging it. |
That is very good news indeed. Any info if this will also address the related issue that a svelte:fragment also triggers the "must be direct child" error message within conditional or loop statements? Also, has anyone any information what is going on with #8535? Dynamic slot names are also sorely missing.
|
This will be more ergonomic in Svelte 5 using the new snippets feature: preview playground link. Slots will be deprecated, as such this feature won't be implemented in slots, but as shown in the preview playground it's easily achievable using snippets. |
Hi, Demo.svelte
This means that when you have something like this:
Hope it helps |
@dummdidumm Is there any outlook of whether this syntax will end up being supported? It's cool that snippets enable it to be achieved via props, but being able to handle it inside the component body would be nice as well. |
+1 |
I'm adding here my workaround, in case anybody find it useful I'm trying to conditionally forward a slot (meaning, if the slot hasn't been specified, I don't want to forward any slot at all)
the work around I found (ugly in deed but it seems to work and to be general enough) is to allow to receive a like this: <!-- Title.svelte -->
<script>
export let slots = $$slots // allow to override $$slots
</script>
<div>
{#if slots.title}
<h3><slot name="title" /></h3>
{/if}
<slot />
</div> And this is how I use it to define an <!-- Title.svelte -->
<script>
import Title from './Title.svelte'
export let name = ''
</script>
<!-- override $$slots -->
<Title slots={$$slots}>
<slot slot="title" name="title" />
<input {name} placeholder={name} />
</Title> In this case the name of the slot ( <!-- if the slot name in the child component is not the same as the parent, I can map them like this: -->
<Title slots={{child_slot: $$slots.parent_slot}}> It's clunky, but I hope this one gets merged soon so I can get rid of this ugly workaround here's a working repl: https://svelte.dev/repl/3c82d89f3b564bce82760aba3f5c9b44?version=4.2.12 |
Is your feature request related to a problem? Please describe.
When I do something like the below, I get an error of:
Describe the solution you'd like
I want to be able to do the first example and wrap my optional slot's in if statements. The reason for this is I actually have default slot text in my component that I want to show.
Describe alternatives you've considered
The alternatives is doing something like this which has a lot of duplicate code.
How important is this feature to you?
This is a big hassle for me and it would make for a lot cleaner code if I could wrap this slots in if statements. However, it would not affect my ability to code and use svelte.
The text was updated successfully, but these errors were encountered: