Search code examples
vue.jsvuejs3slots

Vue slots within a loop for a slider component


I'm having a bit of trouble working out how to use slots for a SliderA component.

SliderA component looks like the following with slides being an array prop.

<template>
    <div class="slider-container" ref="container">
        <div class="viewport" ref="viewport">
            <div class="slider" ref="slider">
                <div v-for="(slide, index) in slides" :key="index" class="slide">
                    <slot :name="`example-${slide._id}`"></slot>
                </div>
            </div>
        </div>
    </div>
</template>

And on the page itself I am doing the following:

<SliderA :slides="data.homePage.featuredGenres">
    <template v-for="genre in data.homePage.featuredGenres" v-slot:[`example-${genre._id}`] :key="genre._id">
        <div class="genre">
            ...
        </div>
    </template>
</SliderA>

This seems to be working but I have a few concerns.

  1. It breaks if I put the :key on the <div class="genre"></div> element within the template loop. The error states the key must be placed on the template tag. Confusing.

  2. I am passing in the data (an array of objects) to the component as a prop :slides="data.homePage.featuredGenres" in order to loop through the slots within the component which feels unnecessary but can't work out another way to do it.

UPDATE

I tried the following which seems to work but feels like there's a simpler way. Plus, the CSS within the component don't render (or flash off) on load. I think this would require a minimal reproduction but might just have to try and make it less efficient.

<SliderA :slides="data.homePage.featuredGenres">
    <template v-for="genre in data.homePage.featuredGenres" v-slot:[`example-${genre._id}`] :key="genre._id">
        <GenreItem :genre="genre" />
    </template>
</SliderA>

Solution

  • You need scoped slots (you pass data to the slot from its component).

    And read documentation for key in v-for.

    You use a default slot here, no need in named slots.

    VUE SFC PLAYGROUND

    #="{slide: genre}" here is a shortcut for <template v-slot="{slide: genre}"> for the default slot.

    <script setup>
    import SliderA from './SliderA.vue';
    
    const data = {
      homePage:{
        featuredGenres: [{name: 'Trash metal'}, {name: "Drum'n'bass"}]
      }
    };
    </script>
    
    <template>
    <SliderA :slides="data.homePage.featuredGenres" #="{slide: genre}">
      <div class="genre">
          {{ genre.name }}
      </div>
    </SliderA>
    </template>
    
    

    SliderA.vue

    <script setup>
    defineProps({
      slides: Array
    })
    </script>
    
    
    <template>
        <div class="slider-container" ref="container">
            <div class="viewport" ref="viewport">
                <div class="slider" ref="slider">
                    <div v-for="(slide, index) in slides" :key="index" class="slide">
                        <slot :="{slide}"></slot>
                    </div>
                </div>
            </div>
        </div>
    </template>