sveltejs / svelte

web development for the rest of us
https://svelte.dev
MIT License
80.34k stars 4.28k forks source link

Feature Suggestion: Improve versatility of `<svelte:component>` and `<svelte:element>` by treating them as a `<svelte:fragment>` when the `this` prop value is present but is a falsy or nullish value #7437

Closed brandonmcconnell closed 8 months ago

brandonmcconnell commented 2 years ago

Describe the problem

In some cases, I would like to conditionally render some content in a component or element.

Describe the proposed solution

For these cases, it would be useful if we were able to conditionally omit the component or element by using a nullish or falsy value in the this prop. So this example (below) would simply render Some text since the this prop is a falsy value:

<svelte:component this={false && MyComponent}>
  Some text
</svelte:component>

In such cases, the <svelte:component> or <svelte:element> would simply be treated as a <svelte:fragment>.

Alternatives considered

The main alternative is see is similar but essentially creating my own Fragment component which simply returns the slotted content in this format:

<slot {...$$restProps} />

In practice, it could be used like this:

<svelte:component
  this={condition
    ? WrapperComponent
    : Fragment
  }
>
  Some text
</svelte:component>

The downside to this, aside from having to import another custom component is that it would not work at all for <svelte:element>. In those cases, I would actually have to create a Svelte component counterpart for every single element so I could use them against my new Fragment component like this:

<script>
  import { Fragment, Div } from './customComponents.js';
  const condition = Math.random() < 0.5;
</script>

<svelte:component
  this={condition
    ? Div
    : Fragment
  }
>
  Some text
</svelte:component>

Importance

would make my life easier

lukaszpolowczyk commented 2 years ago

I second this, as I suggested something similar: https://github.com/sveltejs/svelte/pull/6898#issuecomment-1046275644 In my case it would be this="svelte:fragment". :)

dimfeld commented 2 years ago

Rendering the slot by itself when this is falsy would be a breaking change in Svelte. Less important perhaps for svelte:element since it's new but svelte:component has been around for a long time. Something like a Fragment component seems fine for that case.

For svelte:element, I like @lukaszpolowczyk's idea of using "svelte:fragment" to trigger the "render the slot with no wrapping element" behavior.

AlbertMarashi commented 2 years ago

@dimfeld I believe part of the purpose of svelte:element is to be able to bind to a dynamically created element within svelte.

I am curious why you can't use an #if block to do this, as it's only two possibilities, as compared to dozens of HTML elements

dimfeld commented 2 years ago

Contrived example and you could definitely make a component to make it easier to manage, but basically this:

{#if element}
  <svelte:element this={element}>
    500 lines of markup
  </svelte:element>
{:else}
  same 500 lines of markup
{/if}
brandonmcconnell commented 2 years ago

Rendering the slot by itself when this is falsy would be a breaking change in Svelte. Less important perhaps for svelte:element since it's new but svelte:component has been around for a long time. Something like a Fragment component seems fine for that case.

For svelte:element, I like @lukaszpolowczyk's idea of using "svelte:fragment" to trigger the "render the slot with no wrapping element" behavior.

I may be mistaken here, but wouldn't it constitute as a breaking change if this change were to break projects that are currently working?

I believe this change would do the opposite; it would not break any working codebases, but it would cause Svelte to see this={condition && MyComponent}, as valid where it presently throws an error in such cases (when the returned value is falsy or nullish). Any codebases that currently employ this syntax would be potentially erroneous already; this change would change that to make it an acceptable syntax.

(Again, I may very we'll be mistaken here ☝🏼)

Yes, I also really like @lukaszpolowczyk's idea of being able to use svelte:fragment as the this value, and I think it could be valuable if supported for both svelte:component as well as the new svelte:element. I opened another feature request related to this specifically (#7396), but it was closed with the reasoning that svelte:fragment is not an actual variable or class that can be referenced at runtime, though I'd think the compiler could be adjusted to accommodate for a change like this. This issue birthed from that issue's resolution.

dimfeld commented 2 years ago

The current (and officially documented) behavior of svelte:component is to render nothing if this is falsy. See https://svelte.dev/repl/654a4be7e34e4aec81a6fb48c5e9a30c?version=3.47.0

brandonmcconnell commented 2 years ago

Great call-out, @dimfeld. I was mistaken here and thought I had seen a similar example throw an error.

My suggestion would then be to support svelte:fragment per my previous request (#7396), and @lukaszpolowczyk's suggestion in #6898.

Seeing as those were closed and this current suggestion would be a breaking change, I will close this ticket. Thanks, all. 👋🏼

ghost commented 2 years ago

I agree there should be a way of conditionally rendering some content in a component or element with a single tag. I'm currently using the svelte component below to handle this, but a built-in solution would be preferable.

<script>
  export { this_ as this }

  let this_
</script>

{#if this_ === '#'}
  <slot />
{:else if typeof this_ === 'string'}
  <svelte:element this={this_} {...$$restProps}>
    <slot />
  </svelte:element>
{:else}
  <svelte:component this={this_} {...$$restProps}>
    <slot />
  </svelte:component>
{/if}
janosh commented 1 year ago

Curious if anyone has an argument against

<svelte:element this="svelte:fragment" /> == <svelte:fragment />
Rich-Harris commented 8 months ago

We definitely don't want to do this, it would be a confusing change in behaviour. In Svelte 5, we have snippets which provide much more control over this sort of thing.