vue.jsvuejs3vuejs-slots

Is there no way to reliably work with slot content in Vue 3?


I am currently migrating a Vue 2 codebase to Vue 3.

<card>
    <template #default>
        <card-header>
            <title level="3">Header</title>
        </card-header>
        <card-body
            v-for="b in bodyCount"
            :key="'b' + b">
            Body {{ b }}
        </card-body>
        <card-footer
            v-for="f in footerCount"
            :key="'f' + f">
            <text>Footer {{ f }}</text>
        </card-footer>
    </template>
</card>

Card component has a render function which calls this.$slots.default(). However in Vue 3 this returns different content to what it did in Vue 2. So if I do console.log(this.$slots.default()) - I get an array with 3 elements, [v-node with type: 'header', v-node with type: Symbol(Fragment), v-node with type: Symbol(Fragment)]

More specifically it does not identify card-footer/card-body as components but rather I see Symbol (Fragment) in the type. Card-header is actually fine because it is not in a v-for loop.

There is logic inside the render function of the card component which needs to know how many body/footer components it has. So I need to be able to tell the component types. Is this not possible in Vue 3? I can guess that this is related to the v-for here but I am not sure how to actually get the end result v-nodes. In this example I would have expected the array to contain the card-header + all card bodies + all card footers instead of 3 elements.


Solution

  • As it turns out - Fragments can be unwrapped through the children property on each element of the array. So v-for in this case results in a v-node which is a fragment but we can infer further content by looking at the children property.