如何在 Svelte 中修改父组件的子组件插槽
How to modifying child component's slots from parent components in Svelte
我正在使用这种用法在 Svelte 中开发一个 DataTable
组件:
<script>
import DataTable from './DataTable.svelte'
import Column from './Column.svelte'
let items = [
{id: 1, name: 'Name 1', age: 23},
{id: 2, name: 'Name 2', age: 28},
{id: 3, name: 'Name 3', age: 35},
{id: 4, name: 'Name 4', age: 18},
]
</script>
<DataTable {items} let:item>
<Column title="Id">{item.id}</Column>
<Column title="Customer">{item.name}</Column>
<Column title="Age">{item.age}</Column>
</DataTable>
Column
组件只是作为 DataTable
组件的元定义。在 script
部分(如 this)中还有一些其他方法可以定义 columns
,但为了更专注于 script
部分中的业务逻辑,我正在尝试在 script
部分减少组件的视觉效果 definitions/configurations。
因此,Column
组件中的默认 slot
内容应传递给父 DataTable
组件,然后在单元格中呈现。换句话说,父组件应该控制子组件的插槽。
DataTable.svelte:
<script>
import { setContext, getContext, onDestroy } from 'svelte';
export let items = []
let columns = []
setContext('columns', {
register: column => {
columns.push(column)
onDestroy(() => {
const i = columns.indexOf(column)
columns.splice(i, 1)
})
},
getColumns: () => {return columns}
})
</script>
<slot />
{#if items}
<table width="100%" border="1">
<thead>
<tr>
{#each columns as column}
<th>
{column.title}
</th>
{/each}
</tr>
</thead>
{#each items as item1, i}
<tr>
{#each columns as column}
<td>
{JSON.stringify(column.$$scope.ctx[0][i])}
</td>
{/each}
</tr>
{/each}
</table>
{/if}
Column.svelte:
<script>
import { getContext } from 'svelte';
let columnProps = {...$$props}
const { register } = getContext('columns')
register(columnProps)
</script>
在这一行中:{JSON.stringify(column.$$scope.ctx[0][i])}
绑定行显示正确(不确定它是否适用于所有情况,因为索引 0 是硬编码的!)。
但是,内容应该使用第一列、第二列等列的槽({item.id}
、{item.name}
等)呈现
如何动态渲染插槽或在父组件中渲染插槽以显示正确的插槽内容?
此外,由于在上述用法中将插槽值传递给 Column
(子项中没有任何插槽),Svelte 会发出警告 (<Column> received an unexpected slot "default".)
,这是完全正确的!
实施此 DataTable
组件的正确方法是什么?
正确的做法可能是完全不这样做。我致力于以类似的方式实现选项卡控件,而 Svelte 似乎不太擅长与在多个地方使用的 slots/child 组件进行交互。
要使这项工作在某种程度上起作用,您可以进行一些更改:
- 渲染
Column
中的单元格:
<td>
<slot />
</td>
- 通过在
DataTable
中插入槽来渲染整行(它已经包含所有列):
{#each items as item, i}
<tr>
<slot {item} />
</tr>
{/each}
删除 DataTable
中 <script>
标签后的流氓 <slot/>
,因为它没有 item
.
重新分配 columns
,否则它不会更新;还要检查该列是否已注册,否则会重复(每行一个):
register: column => {
if (columns.find(c => c.title == column.title))
return;
columns = [...columns, column];
onDestroy(() => {
columns = columns.filter(c => c.title != column.title);
})
},
一旦您认为 title
应该是任意 DOM 内容而不仅仅是一个字符串,您可能会后悔。
你绝对不应该访问像 $$scope.ctx
这样的东西,这是一个可以随时更改的实现细节。
我正在使用这种用法在 Svelte 中开发一个 DataTable
组件:
<script>
import DataTable from './DataTable.svelte'
import Column from './Column.svelte'
let items = [
{id: 1, name: 'Name 1', age: 23},
{id: 2, name: 'Name 2', age: 28},
{id: 3, name: 'Name 3', age: 35},
{id: 4, name: 'Name 4', age: 18},
]
</script>
<DataTable {items} let:item>
<Column title="Id">{item.id}</Column>
<Column title="Customer">{item.name}</Column>
<Column title="Age">{item.age}</Column>
</DataTable>
Column
组件只是作为 DataTable
组件的元定义。在 script
部分(如 this)中还有一些其他方法可以定义 columns
,但为了更专注于 script
部分中的业务逻辑,我正在尝试在 script
部分减少组件的视觉效果 definitions/configurations。
因此,Column
组件中的默认 slot
内容应传递给父 DataTable
组件,然后在单元格中呈现。换句话说,父组件应该控制子组件的插槽。
DataTable.svelte:
<script>
import { setContext, getContext, onDestroy } from 'svelte';
export let items = []
let columns = []
setContext('columns', {
register: column => {
columns.push(column)
onDestroy(() => {
const i = columns.indexOf(column)
columns.splice(i, 1)
})
},
getColumns: () => {return columns}
})
</script>
<slot />
{#if items}
<table width="100%" border="1">
<thead>
<tr>
{#each columns as column}
<th>
{column.title}
</th>
{/each}
</tr>
</thead>
{#each items as item1, i}
<tr>
{#each columns as column}
<td>
{JSON.stringify(column.$$scope.ctx[0][i])}
</td>
{/each}
</tr>
{/each}
</table>
{/if}
Column.svelte:
<script>
import { getContext } from 'svelte';
let columnProps = {...$$props}
const { register } = getContext('columns')
register(columnProps)
</script>
在这一行中:{JSON.stringify(column.$$scope.ctx[0][i])}
绑定行显示正确(不确定它是否适用于所有情况,因为索引 0 是硬编码的!)。
但是,内容应该使用第一列、第二列等列的槽({item.id}
、{item.name}
等)呈现
如何动态渲染插槽或在父组件中渲染插槽以显示正确的插槽内容?
此外,由于在上述用法中将插槽值传递给 Column
(子项中没有任何插槽),Svelte 会发出警告 (<Column> received an unexpected slot "default".)
,这是完全正确的!
实施此 DataTable
组件的正确方法是什么?
正确的做法可能是完全不这样做。我致力于以类似的方式实现选项卡控件,而 Svelte 似乎不太擅长与在多个地方使用的 slots/child 组件进行交互。
要使这项工作在某种程度上起作用,您可以进行一些更改:
- 渲染
Column
中的单元格:
<td>
<slot />
</td>
- 通过在
DataTable
中插入槽来渲染整行(它已经包含所有列):
{#each items as item, i}
<tr>
<slot {item} />
</tr>
{/each}
删除
DataTable
中<script>
标签后的流氓<slot/>
,因为它没有item
.重新分配
columns
,否则它不会更新;还要检查该列是否已注册,否则会重复(每行一个):
register: column => {
if (columns.find(c => c.title == column.title))
return;
columns = [...columns, column];
onDestroy(() => {
columns = columns.filter(c => c.title != column.title);
})
},
一旦您认为 title
应该是任意 DOM 内容而不仅仅是一个字符串,您可能会后悔。
你绝对不应该访问像 $$scope.ctx
这样的东西,这是一个可以随时更改的实现细节。