Recursive scoped slots

I have a use case (navigation tree) where I want to have slots replaced in a component and forward that same slots to all the children.

Here is a JSFiddle: https://jsfiddle.net/rayrutjes/gqadsra4/

In this example we see that the slot is correctly overridden in the root recursive-item component, but not in the children ones.
If tried to “duplicate” the slot, but it then indicates that 2 default slots exist in the rendering tree.

Any idea how I can forward a user provided template for a slot to children elements?

PS: the example has been simplified to allow better reasoning. In the real use case, 1 recursive item can have multiple children of type recursive-item.

2 Likes

Hi,

I think, you can solve your problem with a render function. It seems to be possible (I’m still a beginner) to pass the original scoped slots function down to the next invocation of the recursive component.

Here’s a .vue file:

<template>
    <recursive-item :level="1">
        <template scope="props">
            This is lovely level {{props.level}}.
        </template>
    </recursive-item>
</template>

<script>
const RecursiveItem = {
    props: {level: Number},
    render(h) {
        if (this.level < 5) {
            return h('ul', [
                h('li', [
                    this.$scopedSlots.default({level: this.level}),
                    h(RecursiveItem, {
                        props: {level: this.level + 1},
                        scopedSlots: {default: this.$scopedSlots.default}
                    })
                ])
            ]);
        }
    }
}

export default {
    components: {
        RecursiveItem,
    }
}
</script>
```

Stefan
2 Likes

Thanks for the answer @sma, I will give that a try.

I was hoping to find out an easier solution than writting render functions, given my real use case is far more complex than the fiddle share :wink:

I’ll share my feedback here after the attempt.

It worked like a charm @sma

Here is the working version: https://jsfiddle.net/rayrutjes/gqadsra4/4/

Thanks a lot.

It can be done without using render functions, if you don’t need a default template and/or it is acceptable to use a helper component to supply the default template.

Here is my TreeView implementation using the wrapper method:

4 Likes

To anyone passing by, note Paul’s solution on stack overflow, with automatic passing of slot, clean, doesn’t need a wrapper and works great with default:

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

Here is the fiddle.

6 Likes

Thanks, here’s the version with the new syntax as of 2.6.0+

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

Thank you!