javascriptvuejs3vue3-carousel

How to pass template to grandchild carousel's #slides slot (vue3-carousel)


I need to create a BaseSlider component. in this component, I use vue3-carousel. Here is the BaseSlider code:

<template>
    <div class="mv-slide">
        <carousel ref="carousel" v-model="currentSlide" :items-to-show="1">
            <slot />
            <template #addons v-if="arrow">
                <navigation>
                    <template #next>
                        <span> >> </span>
                    </template>
                    <template #prev>
                        <span>
                            dsa </span>
                    </template>
                </navigation>
            </template>
        </carousel>
        <div class="mv-slide__pagination" v-if="indicator">
            <div v-for="slide in 11" :class="{
                'mv-slide__pagination-item': true,
                'mv-slide__pagination-item--active': currentSlide === slide
            }" @click="currentSlide = slide"></div>
        </div>
    </div>
</template>

Here is how I use the BaseSlider

<BaseSlider>
      <Slide v-for="slide in 10" :key="slide">
                <img src="http://localhost:3000/custom-estimate-mv1.png" alt="custom estimate"
                    class="mv-slide___img">
        </Slide>
 </BaseSlider>

My app is crashed with this code with error

[Vue warn]: Unhandled error during execution of render function at <Carousel ref="carousel" modelValue=0 onUpdate:modelValue=fnonUpdate:modelValue ... >

Cannot set properties of null (setting 'index')

at Array.forEach ()

Does anyone have experience on create component wraping vue3-carousel


Solution

  • If you pass the slides over your BaseSlider component, then your default slot gets a parent Vnode containing all your slides as children.

    This means, To access slides you should go over slots.default()[0].children and the Carousel is not prepared for such scenario.

    That's why the errors happens in the Carousel at the line

    slidesElements.forEach((el, index) => (el.props.index = index));
    

    since the parent Vnode does not have props.

    I haven't found any way to fix it using templates, so I wrote a Render Function for the BaseSlider that returns the Carousel and fills the default slot with the slides.

    <script setup>
    import { useSlots, h} from 'vue';
    import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel';
    
    const slots = useSlots()
    const render = () => {
        return  h('div', { class: 'mv-slide' }, [
           h(Carousel, { 'items-to-show': 1 }, {
              default: () => slots.default()[0].children,
              addons: () => [ h(Navigation),  h(Pagination) ]
            })  
        ]);
    };
    </script>
    
    <template>
        <render />
    </template>
    

    Here is the working SFC Playground