将插槽传递到 Vue.js 中的插槽
Passing slot into slot in Vue.js
我正在尝试将插槽传递到插槽中,但是 child 需要向下传递插槽的组件看不到它。
在 child (TheTable) 中,我有来自 Core UI 的 table 组件用于 Vue (CDataTable),它需要我想从 [=23= 传递的某些插槽]:
<CDataTable
class="overflow-auto"
:items="this.items"
:fields="fieldData"
:sorter="{ external: true, resetable: true }"
:sorter-value="this.sorterValue"
:table-filter="{ external: true, lazy: false, placeholder: 'Enter here', label: 'Search:'}"
:responsive="false"
:loading="this.loading"
:hover="true"
:items-per-page-select="true"
:items-per-page="this.perPage"
@pagination-change="paginationChanged"
@update:sorter-value="sorterUpdated"
@update:table-filter-value="filterUpdated"
>
<slot name="requiredTableFields"></slot>
</CDataTable>
在 parent 组件中我有:
<TheTable
v-bind:fields="fields"
v-bind:endpoint-url="endpointUrl"
>
<template slot="requiredTableFields">
<td slot="name" slot-scope="{item}">
<div>{{ item['attributes.email'] }}</div>
<div class="small text-muted">
{{ item['attributes.firstname'] }} {{ item['attributes.lastname'] }}
</div>
</td>
<td slot="registered" slot-scope="{item}">
<template v-if="item['attributes.created_at']">{{ item['attributes.created_at'] }}</template>
<template v-else>Not setup yet</template>
</td>
</template>
</TheTable>
有什么办法让它起作用吗?
干杯,
卡斯帕
将底层槽合并到单个 requiredTableFields
槽中是行不通的,因为没有(简单的)方法可以在子槽合并后将它们重新分离出来。
相反,您可以将插槽分开:
<TheTable ...>
<template v-slot:name="{ item }">
<td>
...
</td>
</template >
<template v-slot:registered="{ item }">
<td>
...
</td>
</template>
</TheTable>
这是将两个作用域插槽 name
和 registered
传递到 TheTable
。
假设您不想将插槽名称硬编码到 TheTable
中,那么您需要迭代 $scopedSlots
以动态包含它们。
<CDataTable ...>
<template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
<slot :name="slotName" v-bind="context" />
</template>
</CDataTable>
对此的一些说明:
x
没有使用,我们只需要遍历插槽名称。
- 与作用域插槽关联的 'data' 被称为
context
并且刚刚被传递。
- 如果插槽不是作用域插槽,它会略有不同。我们将迭代
$slots
并删除所有引用 context
. 的部分
- 在
:name
属性的开头有一个 :
,因为我们要传递一个动态名称。不幸的是,原始问题中的一个插槽也称为 name
,这可能会导致此处出现一些混乱。
-
v-bind="context"
部分类似于 JavaScript 扩展运算符,如果这样可以使它更清晰的话。将其视为 attributes = { name: slotName, ...context }
.
下面是说明上述技术的完整示例。它不使用 CDataTable
但传递插槽的核心原理完全相同。
const Comp2 = {
template: `
<div>
<h4>Left</h4>
<div><slot name="top" item="Red" /></div>
<h4>Right</h4>
<div><slot name="bottom" item="Green" /></div>
</div>
`
}
const Comp1 = {
template: `
<div>
<comp2>
<template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
<slot :name="slotName" v-bind="context" />
</template>
</comp2>
</div>
`,
components: {
Comp2
}
}
new Vue({
el: '#app',
components: {
Comp1
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<div id="app">
<comp1>
<template v-slot:top="{ item }">
<button>Slot 1 - {{ item }}</button>
</template>
<template v-slot:bottom="{ item }">
<button>Slot 2 - {{ item }}</button>
</template>
</comp1>
</div>
我正在尝试将插槽传递到插槽中,但是 child 需要向下传递插槽的组件看不到它。
在 child (TheTable) 中,我有来自 Core UI 的 table 组件用于 Vue (CDataTable),它需要我想从 [=23= 传递的某些插槽]:
<CDataTable
class="overflow-auto"
:items="this.items"
:fields="fieldData"
:sorter="{ external: true, resetable: true }"
:sorter-value="this.sorterValue"
:table-filter="{ external: true, lazy: false, placeholder: 'Enter here', label: 'Search:'}"
:responsive="false"
:loading="this.loading"
:hover="true"
:items-per-page-select="true"
:items-per-page="this.perPage"
@pagination-change="paginationChanged"
@update:sorter-value="sorterUpdated"
@update:table-filter-value="filterUpdated"
>
<slot name="requiredTableFields"></slot>
</CDataTable>
在 parent 组件中我有:
<TheTable
v-bind:fields="fields"
v-bind:endpoint-url="endpointUrl"
>
<template slot="requiredTableFields">
<td slot="name" slot-scope="{item}">
<div>{{ item['attributes.email'] }}</div>
<div class="small text-muted">
{{ item['attributes.firstname'] }} {{ item['attributes.lastname'] }}
</div>
</td>
<td slot="registered" slot-scope="{item}">
<template v-if="item['attributes.created_at']">{{ item['attributes.created_at'] }}</template>
<template v-else>Not setup yet</template>
</td>
</template>
</TheTable>
有什么办法让它起作用吗? 干杯, 卡斯帕
将底层槽合并到单个 requiredTableFields
槽中是行不通的,因为没有(简单的)方法可以在子槽合并后将它们重新分离出来。
相反,您可以将插槽分开:
<TheTable ...>
<template v-slot:name="{ item }">
<td>
...
</td>
</template >
<template v-slot:registered="{ item }">
<td>
...
</td>
</template>
</TheTable>
这是将两个作用域插槽 name
和 registered
传递到 TheTable
。
假设您不想将插槽名称硬编码到 TheTable
中,那么您需要迭代 $scopedSlots
以动态包含它们。
<CDataTable ...>
<template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
<slot :name="slotName" v-bind="context" />
</template>
</CDataTable>
对此的一些说明:
x
没有使用,我们只需要遍历插槽名称。- 与作用域插槽关联的 'data' 被称为
context
并且刚刚被传递。 - 如果插槽不是作用域插槽,它会略有不同。我们将迭代
$slots
并删除所有引用context
. 的部分
- 在
:name
属性的开头有一个:
,因为我们要传递一个动态名称。不幸的是,原始问题中的一个插槽也称为name
,这可能会导致此处出现一些混乱。 -
v-bind="context"
部分类似于 JavaScript 扩展运算符,如果这样可以使它更清晰的话。将其视为attributes = { name: slotName, ...context }
.
下面是说明上述技术的完整示例。它不使用 CDataTable
但传递插槽的核心原理完全相同。
const Comp2 = {
template: `
<div>
<h4>Left</h4>
<div><slot name="top" item="Red" /></div>
<h4>Right</h4>
<div><slot name="bottom" item="Green" /></div>
</div>
`
}
const Comp1 = {
template: `
<div>
<comp2>
<template v-for="(x, slotName) in $scopedSlots" v-slot:[slotName]="context">
<slot :name="slotName" v-bind="context" />
</template>
</comp2>
</div>
`,
components: {
Comp2
}
}
new Vue({
el: '#app',
components: {
Comp1
}
})
<script src="https://unpkg.com/vue@2.6.11/dist/vue.js"></script>
<div id="app">
<comp1>
<template v-slot:top="{ item }">
<button>Slot 1 - {{ item }}</button>
</template>
<template v-slot:bottom="{ item }">
<button>Slot 2 - {{ item }}</button>
</template>
</comp1>
</div>