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

Programmatic Slot API & Slot "Sharing" #3730

Closed
rafegoldberg opened this issue Oct 17, 2019 · 3 comments
Closed

Programmatic Slot API & Slot "Sharing" #3730

rafegoldberg opened this issue Oct 17, 2019 · 3 comments

Comments

@rafegoldberg
Copy link

rafegoldberg commented Oct 17, 2019

🙀 Sandbox Demo

Feature Request

I've been playing around with Svelte as a React/Vue alternative and it is absolutely stunning. That said, I quickly ran in to a few shortcomings of the <slot> implementation. Consider this App

<!-- App.svelte -->
<List items={items} let:item={item}>
  <b slot="item">{item.text}</b>
</List>
<script>
	import List from './components/list.svelte'
 	export let items = [
		{text: 'Top-Level Item A', hash: 'item-a', children: [
			{text: 'Sub-Item A1', hash: 'item-a1'}, 
			{text: 'Sub-Item A2', hash: 'item-a2'}, 
		]},
		{text: 'Top-Level Item B', hash: 'item-b'}, 
	];
</script>

The user very well might expect this to render a deep list in which every item is rendered inside a <b/> tag, per the passed slot. Now let's check out the List component, which uses <svelte:self> to recursively render deeply nested lists:

<!-- components/List.svelte -->
<ul>
	{#each items as item}
		<li>
			<slot name="item" item={item}/>
			{#if item.children}
				<svelte:self items={item.children}/> <!--agh; cannot share slot-->
			{/if}
		</li>
	{/each}
</ul>
<script>
	export let items = [];
</script>

At the moment this renders top-level items correctly. But sub-lists simply display a bunch of empty list items for. A pseudo-fix might be to add some default content to the slot:

<slot name="item" item={item}>{@html item.text}<slot/>

But again, this doesn't actually address the intended use case outlined above!

Preferred Solution

I'd like to be able to access slot contents programmatically. I'd imagine something similar to how Vue, or how Svelte exposes the $$props object. (Which would allow me to bind the slot to each recursive instance!)

Alternatives

I considered using props to pass a custom Svelte component, but it doesn't quite address my core interest of providing a component implementer a dead simple, flexible way to customize a component's content!

Priority

I'm intrigued by Svelte, but not using it in any production apps so this is far from pressing! (But if I were I'd say it's "quite important".)

Context

See also:

@rafegoldberg rafegoldberg changed the title Slot Sharing Programmatic Slot API & Slot Sharing Oct 17, 2019
@rafegoldberg rafegoldberg changed the title Programmatic Slot API & Slot Sharing Programmatic Slot API & Slot "Sharing" Oct 17, 2019
@dxlbnl
Copy link
Contributor

dxlbnl commented Oct 23, 2019

If I'm not mistaken, this is possible.
I've created an example. There can be multiple slots defined where the contents can be passed.
https://svelte.dev/repl/56403fef96c644a7b765a9bdb2547ca0?version=3.12.1

@rafegoldberg
Copy link
Author

rafegoldberg commented Oct 31, 2019

Ohhhh hey, look at that! Thanks @neoel, this is interesting. So I see what I was missing, and I kind of understand what's happening in your example...

<svelte:self items={node.children} let:item={node}>
  <slot item={node}>No slot</slot>
</svelte:self>

It's almost like we need to repass the slot, which makes sense (even if it feels a bit redundant.) What I don't understand is why you're binding let:item={node} to the <svelt:self/> instance — tho when I remove it it incorrectly renders the parent's text content, so I get that it's critical!!! Any chance you'd like to explain this a bit further? Or link to some references or docs for posterity's/my own sake? Thanks for enlightening me.

@dxlbnl
Copy link
Contributor

dxlbnl commented Nov 3, 2019

Good question! Might be nice for the docs to enlighten this further.

So in the setup there's multiple slots. Since this is a recursive component I can imagine it being tricky to oversee all the slots and props passing. But It's making use of slot props

First the App.svelte uses slot props to get the current item into the 'slot template'. This template will be reused for all items in the lists and sublists.

Then in Lists.svelte it iterates over the items, and renders the 'slot template' with the item passed using slot props. And when the item contains children the nested list is rendered. Since this is a component which receives a property and a slot we create another 'slot template' which receives the item binding again. This template is the same template as passed to the initial call, but we can also change this slot:

<svelte:self items={item.children} let:item={item}>
  <!-- This slot will be passed to the nested Lists, and the item from the nested list will be passed to the original 'slot template' -->
	nest <slot {item}>No slot</slot>
</svelte:self>

This will make every nested list have n 'nest' strings before the original slot template.

Hope this clears things up.

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

2 participants