3

I can not loop through the $slots object in Vue 3 to pass all slots from parent to child, the $slots object seems empty in the child component.

How can I loop through the $slots object to pass all parent slots to the child component?

I get this error when I run the code: TypeError: Cannot read properties of null (reading 'key')

TypeError: Cannot read properties of null (reading 'key')

Here is a sandbox about my problem and you can uncomment line 5 to see the complete result: https://codesandbox.io/s/blazing-bush-g7c9h?file=/src/pages/Index.vue

GitHub sample: https://github.com/firibz/vue3slots

parent:

<system-input filled v-model="text" label="input">
  <template v-slot:before>
    <q-icon name="mail" />
  </template>
</system-input>

child:

  <div class="row justify-center">
    <q-input v-model="componentValue" v-bind="$attrs" style="width: 250px">
      <template v-for="(_, slot) of $slots" v-slot:[slot]="scope">
        <slot :name="slot" v-bind="scope"/>
      </template>
    </q-input>
    <q-separator class="full-width" color="secondary" />
    <div class="bg-negative full-width q-pa-lg">slots: {{ $slots }}</div>
    <div class="bg-warning full-width q-pa-lg">slots: {{ $slots.before }}</div>
  </div>
Fariborz Korevli
  • 439
  • 5
  • 16
  • 1
    Your code works fine in this [demo](https://stackblitz.com/edit/quasar-starter-ehpety?file=src%2FApp.vue). – tony19 Feb 09 '22 at 20:54
  • @tony19 your demo is the only place that this code works and I can not understand what is the difference. this code is not working in my demo and is not working in any of my projects. – Fariborz Korevli Feb 09 '22 at 23:57
  • Can you share a link to a GitHub project that reproduces the problem? – tony19 Feb 10 '22 at 00:27
  • @tony19 here is a [git repository](https://github.com/firibz/vue3slots), you can also download the provided sample on codesandbox, – Fariborz Korevli Feb 10 '22 at 14:53

2 Answers2

7

You have to use Object.keys($slots) in order to use slots on v-for.

<q-input v-model="componentValue" v-bind="$attrs" style="width: 250px">
  <template v-for="(slot, index) of Object.keys($slots)" :key="index" v-slot:[slot]>
    <slot :name="slot"></slot>
  </template>
</q-input>
  • This works, but throughs a Typescript error: "Element implicitly has an 'any' type because expression of type 'string | number' can't be used to index type 'Readonly<...". Does anyone know a way around this error without ignoring it? – ggedde Jul 08 '23 at 23:19
0

To prevent TypeScript errors in the template you can get and cast the slot names in the setup.

Ideally, you could get the actual types rather than just cast to 'default'

<template
  v-for="(slot, index) of slotNames"
  :key="index"
  #[slot]
>
  <slot :name="slot" />
</template>

<script lang="ts" setup>
import { useSlots } from "vue";


const slots = useSlots();

// Assert type here to prevent errors in template
const slotNames = Object.keys(slots) as "default"[];

</script>