89

So I've created a simple wrapper component with template like:

<wrapper>
   <b-table v-bind="$attrs" v-on="$listeners"></b-table>
</wrapper>

using $attrs and $listeners to pass down props and events.
Works fine, but how can the wrapper proxy the <b-table> named slots to the child?

Boussadjra Brahim
  • 82,684
  • 19
  • 144
  • 164
user3599803
  • 6,435
  • 17
  • 69
  • 130
  • FYI, this is going to be solved in [this new PR](https://github.com/vuejs/vue/pull/7765). But if you want to have a solution right now, this [github comment](https://github.com/vuejs/vue/pull/7765#issuecomment-396822866) would help. tested: https://jsfiddle.net/jacobgoh101/bptLavov/185/ – Jacob Goh Jun 17 '18 at 00:34

4 Answers4

181

Vue 3

Same as the Vue 2.6 example below except:

  • $listeners has been merged into $attrs so v-on="$listeners" is no longer necessary. See the migration guide.
  • $scopedSlots is now just $slots. See migration guide.

Vue 2.6 (v-slot syntax)

All ordinary slots will be added to scoped slots, so you only need to do this:

<wrapper>
  <b-table 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>
</wrapper>

Vue 2.5

See Paul's answer.


Original answer

You need to specify the slots like this:

<wrapper>
  <b-table v-bind="$attrs" v-on="$listeners">
    <!-- Pass on the default slot -->
    <slot/>

    <!-- Pass on any named slots -->
    <slot name="foo" slot="foo"/>
    <slot name="bar" slot="bar"/>

    <!-- Pass on any scoped slots -->
    <template slot="baz" slot-scope="scope"><slot name="baz" v-bind="scope"/></template>
  </b-table>
</wrapper>

Render function

render(h) {
  const children = Object.keys(this.$slots).map(slot => h('template', { slot }, this.$slots[slot]))
  return h('wrapper', [
    h('b-table', {
      attrs: this.$attrs,
      on: this.$listeners,
      scopedSlots: this.$scopedSlots,
    }, children)
  ])
}

You probably also want to set inheritAttrs to false on the component.

tony19
  • 125,647
  • 18
  • 229
  • 307
Decade Moon
  • 32,968
  • 8
  • 81
  • 101
  • 2
    The answer for "Vue 2.6" doesn't work for me when using a named slot (on vue 2.6.11). If I remove the parts `="scope"` and `v-bind="scope"`, it works for named slots but I can't use it for scoped slots any more :( – pouria Jun 12 '21 at 07:45
  • I'm having the same issue with 2.6. It seems that the issue is with trying to access the scope o non-scoped slots. Sergey's answer below has worked for me. – Excalibaard Sep 24 '21 at 14:47
91

I have been automating the passing of any (and all) slots using v-for, as shown below. The nice thing with this method is that you don't need to know which slots have to be passed on, including the default slot. Any slots passed to the wrapper will be passed on.

<wrapper>
  <b-table v-bind="$attrs" v-on="$listeners">

    <!-- Pass on all named slots -->
    <slot v-for="slot in Object.keys($slots)" :name="slot" :slot="slot"/>

    <!-- Pass on all scoped slots -->
    <template v-for="slot in Object.keys($scopedSlots)" :slot="slot" slot-scope="scope"><slot :name="slot" v-bind="scope"/></template>

  </b-table>
</wrapper>
Paul Bastowski
  • 1,166
  • 1
  • 8
  • 9
  • This line is the magic if you wanna use the scoped slots from parent for vuetify data-tables (you wanna specify the UI for a specific column from parent component wrapping the data-table). `` – JeppePepp May 14 '22 at 09:28
14

Here is updated syntax for vue >2.6 with scoped slots and regular slots, thanks Nikita-Polyakov, link to discussion

<!-- pass through scoped slots -->
<template v-for="(_, scopedSlotName) in $scopedSlots" v-slot:[scopedSlotName]="slotData">
  <slot :name="scopedSlotName" v-bind="slotData" />
</template>

<!-- pass through normal slots -->
<template v-for="(_, slotName) in $slots" v-slot:[slotName]>
  <slot :name="slotName" />
</template>

<!-- after iterating over slots and scopedSlots, you can customize them like this -->
<template v-slot:overrideExample>
    <slot name="overrideExample" />
    <span>This text content goes to overrideExample slot</span>
</template>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Sergey
  • 381
  • 3
  • 7
7

This solution for Vue 3.2 version and above

<template v-for="(_, slot) in $slots" v-slot:[slot]="scope">
    <slot :name="slot" v-bind="scope || {}" />
</template>
nikober
  • 121
  • 2
  • 3
  • This works great except when the slot is dynamically created and rendered as a fragment or text root node... then you get a ton of `Vue Warn`s in the dev console. I ran into this issue when wanting to wrap the Kendo UI Grid component and exposing its cell template slots. – incutonez Jul 07 '22 at 20:11
  • 1
    What does make it specific to 3.2? – Estus Flask Sep 14 '22 at 06:24
  • I'm getting a typescript error at `[slot]`: `Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type {...}`. Any idea how to solve this? – Gannet Nov 16 '22 at 02:47
  • Same here. It does seem to run correctly, as far as I can tell, but VSCode flags it as a type error. If I use this method, ALL of my wrapper components will be flagged in the Explorer as files with errors, potentially hiding real errors down the line... – Partap Davis Aug 18 '23 at 23:36
  • I figured out how to fix the typescript error (by accident): You need to`import` the component you are wrapping, even if it's already globally registered. (e.g. wrapping Quasar's QInput component, you need to add `import { QInput } from 'quasar'` to your component.) – Partap Davis Aug 19 '23 at 02:43