Skip to content
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

Slot renders 'undefined' when passed an undefined variable #3905

Closed
rburnham52 opened this issue Nov 12, 2019 · 11 comments
Closed

Slot renders 'undefined' when passed an undefined variable #3905

rburnham52 opened this issue Nov 12, 2019 · 11 comments

Comments

@rburnham52
Copy link

Describe the bug
Passing an undefined variable to a component that uses <slot/> renders undefined

To Reproduce
https://svelte.dev/repl/d2fd61d90bca4e6f870950f9eb29b76b?version=3.14.0

Expected behavior
Nothing is render in the slot. This matches the behaviour of passing nothing like <Component></Component>

Severity
Not critical but it forces you user of the component to ensure the property passed is initialised rather than the component internally controlling this.

@Conduitry
Copy link
Member

This is intended behavior, and isn't specific to slots. If you have {foo} in any text node and foo is undefined, this will render as "undefined". See #659 for a previous discussion.

@rburnham52
Copy link
Author

@Conduitry is there a way to check to value of a slot to conditionally render it then?

@pngwn
Copy link
Member

pngwn commented Nov 12, 2019

As i mentioned in discord you need to handle this in the parent. The child component isn't responsible for this behaviour the parent is, you have access to the variable right there you can check if it is undefined and choose whether or not to pass that value in yourself.

@ghost
Copy link

ghost commented Nov 12, 2019

@Nitro52,

I do this in the component to avoid having checks all over the place:

<script>
export let foo = undefined;
const optional = x => x || '';
</script>
{optional(foo)}

Adding this functionality to the core would only muddy the waters since there's situations where you actually want a canary "undefined" to show up so that you know right away you've done something wrong.

To me, the power of Svelte is that it doesn't "help" any more than I need it to.

@rburnham52
Copy link
Author

rburnham52 commented Nov 13, 2019

@pngwn not sure i 100% agree with that. Consider if your were developing stand alone component library that others use. In this case it should be the responsibility of the component to perform error handling of it's inputs.

I'm fine with this being the default because it's obvious something is wrong but i think it should be the implementer of the component that decides if this is really an error or is or ok not to show.

@dkondrad so the work around would be to use a prop instead of slots? What if your needing some optional HTML to be injected. doing this directly through a prop or as JS variable seems cumbersome.

<MyComponent header="<h1 class=&quot;sale&quot;>Buy Now</h1><p>Some sales text</p>>">

I think just having an easy sytanx to make a slot optional would solve the problem.

<slot if-value/>
or
<slot optional/>
or 
{#if this.slot}
<slot/>
{/if}

@dimfeld
Copy link
Contributor

dimfeld commented Nov 14, 2019

I think the way to do what you want right now is something like this:

=== Component.svelte
<slot>Default</slot>

=== App.svelte
{#if label}
<Component>{label}</Component> <!-- Renders {label} -->
{:else}
<Component></Component> <!-- Renders "Default" -->
{/if}

While I agree that the parent is the right place to handle this, it might be nice to have some better way to determine if you want the slot rendered or not. Since this doesn't work:

<Component>
 {#if label}{label}{/if}    <!-- Renders nothing, not "Default" -->
</Component>

Off the top of my head, maybe something such as

<Component slot:render={label !== undefined}>
{label}
<div slot="namedSlot" slot:render={otherLabel !== undefined}>{otherLabel}</div>
</Component>

Any thoughts on that?

@rburnham52
Copy link
Author

rburnham52 commented Nov 15, 2019

@dimfeld Why do you think the parent is the right place to handle this? I'm with the opinion that every component should be able to stand in isolation and have full control over it's internal workings.

Personally i would rather this scenario throw a console error than render undefined. The main reason being that this data could be loaded via an API and may not be obvious until it is in production. Showing 'Undefined' to the end user a silent action and is not very useful, most of the time they will not report it unless it really gets in their way.

But more importantly a console error can be caught and reported directly to the developer using tools like Sentry.

At least if there was a way to check if the slot was provided then the component developer could choose how to handle it.

Perhaps your developing an awesome Tabs component that you want to sell. If your users (other developers) are not initialising your component properly a better approach might be to render a nice user friendly water mark in it's place and then log a console error for the developer. Because it won't look good if your component does not handle unexpected situation's in their production environment.

@pngwn
Copy link
Member

pngwn commented Nov 15, 2019

Because the slotted content doesn't belong to the child component, it belongs to the parent. The child just allows it to pass through.

@dimfeld
Copy link
Contributor

dimfeld commented Nov 15, 2019

In a simple example, perhaps <Component>{label}</Component> there's not a lot to think about regarding the child component. But let's take your other example, but with a slot and some templating:

<Component>
  <h1 class="sale">{sale_label}</h1><p>{sales_text}</p>
</Component>

You effectively have arbitrary content that the component needs to make a decision about. It's not really tenable.

Again, without thinking about it a lot or being intimately familiar with Svelte's internals, I see a couple possible changes here that might be helpful:

  1. Maybe a flag on a slot that could fall back to the default if the parent-provided content renders to nothing. I guess this is the same as your <slot optional>... suggestion. I'm not sure how this would interact with whitespace but presumably that's not a major problem.
  2. I am sympathetic to the idea that an undefined value should raise an error. The current behavior is simply because JS coerces undefined to the string "undefined". Changing this behavior is really more of a feature of the templating system though than anything related specifically to slots.

I should note that right now you could do something like {label || ''} or with latest Babel and the right plugins {label ?? '' } (assuming that works in a template?). Of course it's a bit of a hassle to write that everywhere and you lose any sort of error checking, should the behavior around undefined change.

@rburnham52
Copy link
Author

@pngwn yeah i get that the content does not belong to the child component, but the child component should know if not providing content for one of it's slots is valid or not. It does not matter what that content is only that it may or may not exist.

Take this example
https://svelte.dev/repl/4e8c9a0623254b5b83f3372858563209?version=3.14.1

Now i agree that there are technically 2 bugs here.

  1. The app.svelte component should handle not receiving a sale.offerHtml
  2. The API should probably be returning a sale.offHtml

But the SalesBanner component knows that if it does not have a slot content then it really should not show and probably throw an error. A third party component developer can not always rely on it's component inputs being initialised correctly.

As it is right now if this slipped through to production it would be a silent bug. either way as @dimfeld is suggesting i think it would good be to have an enhancement somewhere to be able to better handle the situation.

@rburnham52
Copy link
Author

Thinking about it there is already a similar syntax that i think would solve the issue but it does not work on slots. But if you could get a reference to the slot it would open a lot of doors

<script> 
	let description;
</script>
{#if description}
<article >
	<h1>
		Hot Offer!
	</h1>
	<div>
		<slot bind:this={description}></slot> 
	</div>
</article>
{/if}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants