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:

  1. Assign v-bind to $attrs on b-table, which binds any attributes from the parent:
<b-table v-bind="$attrs" ...>
  1. Assign v-on to $listeners on b-table, which attaches any event listeners from the parent
<b-table v-on="$listeners" ...>
  1. 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>

Edit Wrapper for b-table


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).

Tags:

Vue.Js

Vuejs2