How to pass cell templates to a component with b-table?

You can use Dynamic Slot Names feature of Vue 2 to pass all (or some) slots from parent to <b-table> inside child like this:

Child:

<b-table>
  <template v-for="(_, slotName) of $scopedSlots" v-slot:[slotName]="scope">
    <slot :name="slotName" v-bind="scope"/>
  </template>
</b-table>

$scopedSlots contains all slots passed to your component.

Now this will work:

    <MyTableComponent :items="items" :fields="fields">
          <template #cell(field1)="data">
            {{ data.item.value }}
          </template>
    </ MyTableComponent>

UPDATE 2 – Vue 3

To make above code work in Vue 3, just replace $scopedSlots with $slots as suggested by migration guide

UPDATE 1

You can filter $scopedSlots if you want (have some slot specific to your wrapper component you don’t want to pass down to <b-table>) by creating computed

I mentioned this possibility in my original answer but it is a bit problematic so it deserves better explanation…

  1. Scoped slots are passed to a component as a functions (generating VNode’s when called). Target component just executes those she knows about (by name) and ignores the rest. So lets say your wrapper has b-table (or v-data-table for Vuetify) inside and some other component, let’s say pagination. You can use code above inside both of them, passing all slots to each. Unless there is some naming conflict (both components using same slot name), it will work just fine and does not induce any additional cost (all slot functions are already compiled/created when passed to your wrapper component). Target component will use (execute) only the slots it knows by name.

  2. If there is possible naming conflict, it can be solved by using some naming convention like prefixing slot names intended just for b-table with something like table-- and doing filtering inside but be aware that $scopedSlots object does contain some Vue internal properties which must be copied along !! ($stable, $key and $hasNormal for Vue 2 – see the code). So the filtering code below even it’s perfectly fine and doesn’t throw any error will not work (b-table will not recognize and use the slots)

<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;
    },
  },

This code can be fixed by including the properties mentioned above but this just too much dependency on Vue internal implementation for my taste and I do not recommend it. If it’s possible, stick with the scenario 1…

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top