如何将单元格模板传递给带有 b-table 的组件?
Posted
技术标签:
【中文标题】如何将单元格模板传递给带有 b-table 的组件?【英文标题】:How to pass cell templates to a component with b-table? 【发布时间】:2021-02-14 14:15:03 【问题描述】:我创建了一个显示各个页面的表格数据的组件。该组件在内部使用 b-table。现在对于几个页面,我想自定义一些列的呈现,并且引导表允许使用具有特殊语法的范围字段槽:
<template #cell(field)="data">
data.item.value
</template>
where 字段 - 列名,来自我的包含列的数组,以及 data.item - 要呈现的单元格项目。
问题是我对不同的页面有不同的字段,所以这个自定义应该来自父组件,并且这些模板应该是动态创建的。
这是我尝试解决的方法:
通过属性向 MyTableComponent 传递一个具有可自定义字段和唯一插槽名称的数组 在 MyTableComponent 中动态创建自定义模板,在里面动态创建命名槽
从父传递槽数据到命名槽
MyTableComponent:
<b-table>
<template v-for="slot in myslots" v-bind="cellAttributes(slot)">
<slot :name="slot.name"></slot>
</template>
</b-table>
<script>
...
computed:
cellAttributes(slot)
return ['#cell(' + slot.field + ')="data"'];
...
</script>
家长:
<MyTableComponent :myslots="myslots" :items="items" :fields="fields">
<template slot="customSlot1">
Hello1
</template>
<template slot="customSlot1">
Hello2
</template>
</MyTableComponent>
<script>
...
items: [...my items...],
fields: [...my columns...],
myslots: [
field: "field1", name: "customSlot1" ,
field: "field2", name: "customSlot2"
]
...
</script>
不幸的是,b-table 组件会忽略我的自定义插槽,就像没有提供它们一样。如果我直接在 MyTableComponent 中指定它,它会起作用:
<b-table>
<template #cell(field1)="data">
data.item.value
</template>
</b-table>
但我需要通过组件属性动态完成。请帮忙。
【问题讨论】:
【参考方案1】:您可以使用 Vue 2 的 Dynamic Slot Names 功能将所有(或部分)插槽从父级传递给子级内部的<b-table>
,如下所示:
孩子:
<b-table>
<template v-for="(_, slotName) of $scopedSlots" v-slot:[slotName]="scope">
<slot :name="slotName" v-bind="scope"/>
</template>
</b-table>
$scopedSlots 包含传递给您的组件的所有插槽。
现在这将起作用:
<MyTableComponent :items="items" :fields="fields">
<template #cell(field1)="data">
data.item.value
</template>
</ MyTableComponent>
更新 2 - Vue 3
要使上述代码在 Vue 3 中工作,只需按照 migration guide 的建议将 $scopedSlots
替换为 $slots
即可
更新 1
如果需要,您可以通过创建
computed
过滤$scopedSlots
(有一些特定于您的包装器组件的插槽,您不想传递给<b-table>
)
我在原始答案中提到了这种可能性,但它有点问题,因此值得更好的解释......
作用域插槽作为函数传递给组件(调用时生成 VNode)。目标组件只执行她知道的(按名称)并忽略其余部分。因此,假设您的包装器内部有 b-table
(或 Vuetify 的 v-data-table
)和其他一些组件,比如说分页。您可以在它们内部使用上面的代码,将所有插槽传递给每个插槽。除非存在一些命名冲突(两个组件使用相同的插槽名称),否则它将正常工作并且不会产生任何额外成本(所有插槽函数在传递给您的包装器组件时已经编译/创建)。目标组件将仅使用(执行)它通过名称知道的插槽。
如果可能存在命名冲突,可以通过使用一些命名约定来解决,例如在专用于 b-table
的插槽名称前加上 table--
之类的前缀并在内部进行过滤,但请注意$scopedSlots
对象确实包含一些必须复制的 Vue 内部属性!(对于 Vue 2,$stable
、$key
和 $hasNormal
- 请参阅 code)。所以下面的过滤代码即使它非常好并且不会抛出任何错误不起作用(b-table
将无法识别和使用插槽)
<b-table>
<template v-for="(_, slotName) of tableSlots" v-slot:[slotName]="scope">
<slot :name="slotName" v-bind="scope"/>
</template>
</b-table>
computed:
tableSlots()
const prefix = "table--";
const raw = this.$scopedSlots;
const filtered = Object.keys(raw)
.filter((key) => key.startsWith(prefix))
.reduce(
(obj, key) => (
...obj,
[key.replace(prefix, "")]: raw[key],
),
);
return filtered;
,
,
这段代码可以通过包含上面提到的属性来修复,但这对我的口味来说过于依赖 Vue 内部实现,我不推荐它。如果可能,请坚持方案 1...
【讨论】:
哇,迈克尔,这真是一个很酷的功能,现在我可以在一个组件中配置我的 b 表,在我的大多数页面中重复使用它,但我仍然可以以几乎任何方式进行调整来自父母的需要。成功了,非常感谢! 确实如此!当我发现这样的事情是可能的时,我有同样的“哇”时刻:) @MichalLevý 你将如何过滤这些 $scopedSlots?我正在寻找一种仅对以table--
开头的插槽执行 v-for 循环的方法。我想知道在计算属性中提供什么逻辑。
@caca_pj 这不是真的需要。作用域插槽作为函数传递给组件(生成 VNode)。目标组件只执行她知道的那些并忽略其余的......
@MichalLevý 考虑你想在不同的地方渲染特定的插槽。沙盒:codesandbox.io/s/keen-bose-yi8x0以上是关于如何将单元格模板传递给带有 b-table 的组件?的主要内容,如果未能解决你的问题,请参考以下文章
如何使 UILabel *not* 将触摸传递给它嵌入的 UITableViewCell?
如何将单元格而不是单元格的值传递给 LibreOffice 函数?