How to extend Vue component with default values for props and slots?
In case you prefer templates, you could create a wrapper component like this:
- Assign
v-bind
to$attrs
onb-table
, which binds any attributes from the parent:
<b-table v-bind="$attrs" ...>
- Assign
v-on
to$listeners
onb-table
, which attaches any event listeners from the parent
<b-table v-on="$listeners" ...>
- Pass any slots to
b-table
:
<b-table ...>
<template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
</b-table>
The result should look similar to this:
<template>
<b-table
your-prop-a
your-prop-b
your-prop-c
v-bind="$attrs"
v-on="$listeners"
>
<template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope">
<slot :name="slot" v-bind="scope" />
</template>
</b-table>
</template>
This situation calls for a functional component. Untested, but try something like this:
my-awesome-table.vue
export default {
functional: true,
render(h, ctx) {
// Get data from the parent component
const {
someItemList,
$t,
} = ctx.parent
return h('b-table', {
// Pass on the full data object
...ctx.data,
// Extend the props
props: {
items: someItemList,
busy: someItemList === null,
emptyText: $t('notFound'),
emptyFilteredText: $t('notFound'),
noSortReset: true,
showEmpty: true,
striped: true,
hover: true,
// Override the above default prop values with any props provided
...ctx.props,
},
}, [
// Provide a default rendering for the table-busy slot
// if one is not provided
!ctx.slots()['table-busy'] && h('div', {
slot: 'table-busy',
staticClass: 'text-center my-3',
}, [
h('b-spinner', { staticClass: 'align-middle' })
],
// Append any additional children
...(ctx.children || [])
])
}
}
Then you can use it like this:
<my-awesome-table
:items="otherList"
:busy="isBusy"
>
</my-awesome-table>
<my-awesome-table>
<div slot="table-busy">My custom busy slot</div>
<div slot="something-else">Some other slot</div>
</my-awesome-table>
Keep in mind that the default prop values that <my-awesome-table>
uses is strongly dependent on the parent component, but it's up to you how tightly-coupled you want it to be.
A disadvantage of this approach is you need to write the render function by hand. The Vue template compiler does have very limited support for functional components, but every time I have attempted to compose a functional component that way I have regretted it (the template can get messy with things that can be expressed in code with ease).