svelte icon indicating copy to clipboard operation
svelte copied to clipboard

Feature: Let slots be wrapped in if statements to avoid "must be a child of a component" error.

Open ghost opened this issue 3 years ago • 46 comments

Is your feature request related to a problem? Please describe.

When I do something like the below, I get an error of:

Element with a slot='...' attribute must be a child of a component or a descendant of a custom element

<MyComponent>
  {#if something()}
    <a slot="right-container">Hi</a>
  {/if}
</MyComponent>

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.

{#if something()}
  <MyComponent>
  {#if something()}
    <a slot="right-container">Hi</a>
  {/if}
  </MyComponent>
{:else}
  <MyComponent />
{/if}

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.

ghost avatar Oct 28 '20 12:10 ghost

I would enjoy this feature.

Is there a workaround for now, to have conditional slots? (Except of copy paste..)

s0me0ther avatar Feb 16 '21 13:02 s0me0ther

+1. this would help make my code less verbose in some cases.

ajschmidt8 avatar Mar 12 '21 16:03 ajschmidt8

+1, Especially useful for slot forwarding!!! Otherwise there is no way to use the deepest slot fallbacks

https://svelte.dev/repl/7941b94f6c6f42df93aba4d5ef543917?version=3.38.2

ash0080 avatar May 17 '21 06:05 ash0080

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.

thislooksfun avatar May 29 '21 20:05 thislooksfun

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 <header> – and its padding – when condition is true.

lukeed avatar Jun 08 '21 00:06 lukeed

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 <slot name="body">... clunky but doable.

gmanfredi avatar Dec 01 '21 05:12 gmanfredi

Hey. It'd be great to get this as a feature.

Even with <svelte:fragment> the $$slots.name resolves to true

harshmandan avatar Dec 10 '21 11:12 harshmandan

Issues like this one, the lack of a <svelte:element> analogous to <svelte:component>, lack of a simple way to forward all events, impossibility of using actions on components, etc. are some of the rough edges that must be fixed so Svelte can actually be said to be a really mature framework. These things should just work.

gustavopch avatar Feb 02 '22 20:02 gustavopch

Appreciate the CSS tip @lukeed. For anyone using tailwindcss you can do this with the following:

<header class="p-4 empty:p-0">

e0 avatar Mar 06 '22 15:03 e0

Well, I currently use this ugly workaround with CSS display instead of if:

<header slot="header" style="display: {data.header.length > 0 ? 'inline' : 'none'}">

(use display depending on your element type, e.g. table-row for table rows etc.)

zdenda-online avatar Mar 22 '22 23:03 zdenda-online

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.

madeleineostoja avatar May 25 '22 03:05 madeleineostoja

Is there any progress on getting this over the line? I really don't think that having workarounds is the best way.

boian-ivanov avatar Jun 17 '22 11:06 boian-ivanov

+1

whatwhywhenandwho avatar Jul 13 '22 13:07 whatwhywhenandwho

This will be really helpful when creating custom component with predefined options and pass fragments to child component.

For example: I have MyTableComponent with some classes for SvelteTable component. To be able to fully use SvelteTable from my MyTableComponent, I need to pass 3 slots from MyTableComponent into MyTableComponent. Sometimes 0 or only 1 fragment will be overrides.

Saibamen avatar Jul 26 '22 10:07 Saibamen

+1

bigonha avatar Aug 05 '22 11:08 bigonha

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 header slot. The header slot has some HTML around it that is rendered conditionally on the slot being filled.

I have component B that that renders component A plus some other stuff. Component B provides a header slot that should be forwarded to component A, plus its own footer slot.

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}

GeoffCox avatar Sep 14 '22 00:09 GeoffCox

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 header slot. The header slot has some HTML around it that is rendered conditionally on the slot being filled.

I have component B that that renders component A plus some other stuff. Component B provides a header slot that should be forwarded to component A, plus its own footer slot.

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}

I don't think this is compatible with using TypeScript unfortunately. image

Not-Jayden avatar Sep 19 '22 18:09 Not-Jayden

Also ran into this issue. Conditional slots would be a very useful feature

AlbertMarashi avatar Sep 23 '22 06:09 AlbertMarashi

I ran into this issue yesterday and spun up a repl to reproduce - https://svelte.dev/repl/b089c2c379e9404596445c16311bd1b9?version=3.50.1.

N00nDay avatar Sep 23 '22 12:09 N00nDay

Jup, just ran into this as well. Would love to see this work as the suggested feature. Please add this, Svelte team! 😭💕

Rolands-Laucis avatar Oct 13 '22 13:10 Rolands-Laucis

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

LowArmour avatar Nov 06 '22 18:11 LowArmour

I add my support for the svelte team to add this feature. It's a common problem that currently needs very elaborate workarounds.

sebmor avatar Nov 13 '22 03:11 sebmor

+1

mikerowe81 avatar Nov 18 '22 18:11 mikerowe81

+1

hinex avatar Nov 18 '22 21:11 hinex

It has been more than 2 years that this feature request has been here, with quite a lot of community backing. Is there a timeline for it or is it even something planned to be implemented? I know that there's quite the push to get SvelteKit v1 out, but is there some hope at least of including this in Svelte at any point in the not too distant future @Conduitry @dummdidumm @benmccann ?

boian-ivanov avatar Nov 20 '22 16:11 boian-ivanov

I'm just another guy who ran into this issue. Please fix it.

x4fingers avatar Nov 21 '22 10:11 x4fingers

+1

HeimMatthias avatar Nov 23 '22 16:11 HeimMatthias

Please don't spam all subscribers to this issue with +1 or the equivelant

madeleineostoja avatar Nov 23 '22 21:11 madeleineostoja

Please don't spam all subscribers to this issue with +1.

I don't think this is spamming. It's upvoting, as it's a feature we really really need :)

whatwhywhenandwho avatar Nov 23 '22 21:11 whatwhywhenandwho

It is spamming. Please use thumbs up to upvote the issue. I will be hiding your comment as it just adds noise and doesn't help figure out how to implement this

benmccann avatar Nov 23 '22 22:11 benmccann

<div hidden={yourCondition} slot="slotName">hide if yourCondition is true else show</div>

This is only working with standard HTML-Elements out of the box. If the child is a custom Component, you could wrap it in a div or modify it to accept a hidden parameter.

The mentioned example would look like this: <MyComponent> <a hidden={something()} slot="right-container">Hi</a> </MyComponent>

🙃

fvjupiter avatar Nov 30 '22 12:11 fvjupiter