In my SvelteKit app I have a component that renders a nested list of items, like so:
Until now I made this component very much tied to the rows it is rendering, but now that I have a second content type on my website that I also want to render nested like this, I want to make it generic. I am basically 90% done with that refactor, except the part where the <slot />
works recursively as well.
To give you an idea, here is the new version of the generic code (I've removed a bunch of logic to keep the code simple):
// lib/components/NestedList.svelte
<script lang="ts">
type T = $$Generic<{
id: number;
children?: T[];
}>;
export let items: T[];
export let level = 1;
export let activeID: number;
</script>
<ul class="level-{level}">
{#each items as item (item.id)}
<li>
<div>
<div class="toggler">
{#if item.children?.length}
<button class="as-link" class:expanded={expanded(item, collapsedItems, activeID)} type="button" on:click={() => toggle(item)} />
{/if}
</div>
<slot {item} />
</div>
{#if item.children?.length && expanded(item, collapsedItems, activeID)}
<svelte:self items={item.children} level={level + 1} {activeID} />
{/if}
</li>
{/each}
</ul>
Basically everything works as expected, except that on deeper levels the rows are missing since the recursive <svelte:self>
component is missing the slot with the item.
The parent page renders this component like so:
<div id="list">
<NestedList items={filteredLocations} activeID={+$page.params.locationId} let:item>
<LocationRow location={item} />
</NestedList>
</div>
So, how can I pass in the slot into the recursive self
component?
This doesn't work correctly by the way, I tried:
<svelte:self items={item.children} level={level + 1} {activeID}>
<slot {item} />
</svelte:self>
It just end up re-rendering the parent item row for all children:
I just noticed that let
works on svelte:self
as well, so you should only have to use that to get the current child:
<svelte:self items={item.children} level={level + 1} {activeID}
let:item={child}>
<slot item={child} />
</svelte:self>
(You do not have to alias the item
to child
, but that would shadow the item
of the #each
.)