-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Description
Describe the problem
Integrating Svelte with certain vanilla JS libraries or components from other systems can be cumbersome due to how only a single component can be defined per file.
Consider e.g. a data table component that allows rendering its cells via callback functions that get passed data which is fetched asynchronously from a server. To use Svelte for rendering the cells, you can define a new component for each cell or bundle all the logic into one component and use an {#if}
cascade with some flag that identifies the cell to render.
A hypothetical example:
import NameCell from './NameCell.svelte';
import DescriptionCell from './DescriptionCell.svelte';
import ActionsCell from './ActionsCell.svelte';
function tableAction(node) {
createDataTable({
table: node,
dataSource: '/api/data',
columns: [
{ title: 'Name', render: row => renderCell(row, NameCell) },
{ title: 'Description', render: row => renderCell(row, DescriptionCell) },
{ title: 'Actions', render: row => renderCell(row, ActionsCell) },
],
});
}
function renderCell(row, component) {
const cell = document.createElement('div');
new component({ target: cell, props: { row } });
return cell;
}
<!-- NameCell.svelte -->
<script>
export let row;
</script>
{row.first} {row.last}
<!-- DescriptionCell.svelte -->
<script>
export let row;
</script>
{row.description}
<!-- ActionsCell.svelte -->
<script>
export let row;
</script>
<a href="/user/{row.id}">Details</a>
Describe the proposed solution
Allow defining components within a Svelte file, e.g. by reusing svelte:fragment
or another special element:
<script>
let NameCell, DescriptionCell, ActionsCell;
function tableAction(node) {
createDataTable({
table: node,
dataSource: '/api/data',
columns: [
{ title: 'Name', render: row => renderCell(row, NameCell) },
{ title: 'Description', render: row => renderCell(row, DescriptionCell) },
{ title: 'Actions', render: row => renderCell(row, ActionsCell) },
],
});
}
function renderCell(row, component) {
const cell = document.createElement('div');
new component({ target: cell, props: { row } });
return cell;
}
</script>
<svelte:fragment bind:this={NameCell} let:row>
{row.first} {row.last}
</svelte:fragment>
<svelte:fragment bind:this={DescriptionCell} let:row>
{row.description}
</svelte:fragment>
<svelte:fragment bind:this={ActionsCell} let:row>
<a href="/user/{row.id}">Details</a>
</svelte:fragment>
bind:this
would cause the fragment to be compiled to a component that is then assigned to the referenced variable.let
bindings are analogous to props (export let ...
) on regular components- Events forwarded with
on:event
could be handled on the component instance via$on
and the other API instance functions should work the same as for any component if possible
Ideally it would be possible to also use this to just extract elements locally, e.g. if the hierarchy needs to be dynamic but you do not want to separate common parts into extra files, e.g.:
<script>
export let data;
let Content;
let inList = /* some logic */;
</script>
<svelte:fragment bind:this={Content}>
<h2>{data.title}</h2>
<p>Items: {data.length}</p>
{#each data as item}
<div>...</div>
{/each}
</svelte:fragment>
{#if inList}
<ul>
<li><svelte:component this={Content} /></li>
</ul>
{:else}
<svelte:component this={Content} />
{/if}
Or maybe it would be possible to assign a name
so the extra binding and svelte:component
becomes unnecessary:
<script>
export let data;
let inList = /* some logic */;
</script>
<svelte:fragment name="Content">
<h2>{data.title}</h2>
...
</svelte:fragment>
{#if inList}
<ul>
<li><Content /></li>
</ul>
{:else}
<Content />
{/if}
Alternatives considered
The {#if}
cascade in a single component is probably the best current workaround if many separate components would have to be created otherwise.
For the above example that would be something like:
<script>
export let row;
export let cell;
</script>
{#if cell == 'name'}
{row.first} {row.last}
{:else if cell == 'description'}
{row.description}
{:else if cell == 'action'}
<a href="/user/{row.id}">Details</a>
{/if}
Importance
would make my life easier