BootstrapVue: What if I want to refactor scoped field slots in tables
TL;DR
- You can use scoped field slots to render what a cell in a table should look.
- The
#
sign in a component property is a shorten alias of thev-slot
statement. Therefore,#cell(index)
is a shorten alias ofv-slot:cell(index)
. - If you need to loop scoped field slots, consider using the
render
function. - Vue uses virtual DOM to compile codes, so does the
render
function. v-slot
uses thescopedSlots
property in therender
function, sov-slot:cell(index)
will be complied toscopedSlots: { "cell(index)": () => {} }
.
First: The problem
Have you used BootstrapVue before? If so, you may have known that you can control what a cell should look like in a table by using scoped field slots.
Simply said, you can use <template #cell(prop)="">
on <b-table>
template; and add a ["prop"]
item to fields
property. Like example shows:
<template>
<div>
<b-table small :fields="fields" :items="items" responsive="sm">
<!-- A virtual column -->
<template #cell(index)="data">
{{ data.index + 1 }}
</template>
<!-- A custom formatted column -->
<template #cell(name)="data">
<b class="text-info">{{ data.value.last.toUpperCase() }}</b>, <b>{{ data.value.first }}</b>
</template>
<!-- A virtual composite column -->
<template #cell(nameage)="data">
{{ data.item.name.first }} is {{ data.item.age }} years old
</template>
<!-- Optional default data cell scoped slot -->
<template #cell()="data">
<i>{{ data.value }}</i>
</template>
</b-table>
</div>
</template>
<script>
export default {
data() {
return {
fields: [
// A virtual column that doesn't exist in items
'index',
// A column that needs custom formatting
{ key: 'name', label: 'Full Name' },
// A regular column
'age',
// A regular column
'sex',
// A virtual column made up from two fields
{ key: 'nameage', label: 'First name and age' }
],
items: [
{ name: { first: 'John', last: 'Doe' }, sex: 'Male', age: 42 },
{ name: { first: 'Jane', last: 'Doe' }, sex: 'Female', age: 36 },
{ name: { first: 'Rubin', last: 'Kincade' }, sex: 'Male', age: 73 },
{ name: { first: 'Shirley', last: 'Partridge' }, sex: 'Female', age: 62 }
]
}
}
}
</script>
But what if your scoped field slots of a table are more and more? How to maintain it when you have more than thirty scoped field slots of the table?
I found that I can't use v-bind
and v-for
to make slots into an iterable array. Therefore, I should consider the render
function.
Second: What exactly the render
function is
Like React, Vue uses Virtual DOM to build the whole website structure from version 2.
So this code:
<i class="foo">{{ value }}</i>
Will be complied to:
function (createElement) {
return createElement("i", { class: ["foo"] }, this.value);
}
Third: Exercise
But how about the table I mentioned before? Will the code in the first paragraph:
<template>
<div>
<b-table>
<template #cell(index)="data">
{{ data.index + 1 }}
</template>
</b-table>
</div>
</template>
look like this?
createElement("div", [
createElement("b-table", { /* bypass */ }, [
createElement("template", { "#cell(index)": "data" }, data.index + 1)
])
]);
Hint: type npm run build
to see the result.
Fourth: The answer
The answer is NO.
Actually, the code from the complied file is more like this:
t("div", [
t("b-table", {
scopedSlots: e._u([
{
key: "cell(index)",
fn: function (n) { return [e._v(" " + e._s(n.index + 1) + " ")] }
},
])
})
], 1)
The compressed code shows that the #
sign in a component property means v-slot
, which complied in the scopedSlots
property.
And now, you know what it should look like when putting them in the render function based on the manual:
{
render: function (createElement) {
return createElement("div", [
createElement("b-table", {
scopedSlots: {
"cell(index)": data => createElement("span", data.index + 1)
}
})
])
}
}
Now you can use "the full programmatic power of JavaScript":
{
render: function (createElement) {
const scopedSlots = {
"cell(index)": data => createElement("span", data.index + 1)
};
return createElement("div", [
createElement("b-table", { scopedSlots })
])
}
}
If you are in interested now, how about turning an array to scopedSlots
by using Object.fromEntries()
method?