javascriptvue.jsvuejs3vuejs-slots

TypeError: Converting circular structure to JSON when accessing $slot data from a Child component in VueJS 3


Problem: <pre>{{ $slots.title() }}</pre> is not printing out the expected object.

My parent component

ModalView.vue

<script setup>
import { ref } from 'vue'
import Modal from '@/components/Modal.vue'
import { btnStyleDark } from '@/styles/tailwindStyles'

const showModal = ref(false)
</script>

<template>
  <div>
    <h1>Modals</h1>
    <button @click="showModal = true" :class="btnStyleDark">Show Modal</button>

    <Modal v-if="showModal" @close="showModal = false" :btn-style="btnStyleDark">
      <template #title>My new title</template>
      <p>
        Lorem, ipsum dolor sit amet consectetur adipisicing elit. Vel tempora quibusdam tenetur,
        debitis autem fugit a nesciunt mollitia ipsa inventore nostrum perspiciatis neque dolor id
        magnam facere eum similique? Adipisci.
      </p>
    </Modal>
  </div>
</template>

My child component

Modal.vue

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  btnStyle: String,
})

const emit = defineEmits(['close'])
</script>

<template>
  <teleport to="#modals-container">
    <div class="modal z-10 absolute top-0 left-0 w-full h-full p-10 bg-yellow-50">
      <h1><slot name="title" /></h1>
      <slot />
      <pre>{{ $slots.title() }}</pre>
      <button :class="btnStyle" @click="$emit('close')">Hide modal</button>
    </div>
  </teleport>
</template>

Error in chrome:

TypeError: Converting circular structure to JSON --> starting at object with constructor 'Object' | property 'vnode' -> object with constructor 'Object' --- property 'component' closes the circle at JSON.stringify () at toDisplayString (chunk-U6BEPC57.js?v=60065120:256:154)

If I remove the () from <pre>{{ $slots.title() }}</pre> inside the child I am able to see this printed in the <pre> tag on the page without an error:

enter image description here

However this data does not tell me anything and also does not match the data that is printed out from this course video:

enter image description here

What I've tried


Using the useSlots import

This produces the same error, but the first console.log does work!

<script setup>
import { defineProps, defineEmits, useSlots, onMounted } from 'vue'

const props = defineProps({
  btnStyle: String,
})

const emit = defineEmits(['close'])

const slots = useSlots()

console.log(slots.title())

onMounted(() => {
  console.log(slots)
})
</script>

<template>
  <teleport to="#modals-container">
    <div class="modal z-10 absolute top-0 left-0 w-full h-full p-10 bg-yellow-50">
      <h1><slot name="title" /></h1>
      <slot />
      <pre>{{ $slots.title() }}</pre>
      <!-- <pre>{{ slots.title ? slots.title()[0] : '' }}</pre> -->

      <button :class="btnStyle" @click="$emit('close')">Hide modal</button>
    </div>
  </teleport>
</template>

I also tried <pre>{{ slots.title() }}</pre> without the $ and this still throws the error.


Solution

  • It seems it doesn't like to handle null values when it needs to display.

    I use const slots = useSlots();

    <div v-for="(value, key) in slots.title()[0]" :key="key">
      <strong>{{ key.toString() }}:</strong>
      {{ value ? value.toString() : "null" }}
    </div>`
    

    In my test phase, I created a simple function:

    <button @click="close">Hide modal</button>
    
    function close() {
      console.log(slots.title()[0]);
      emit("close");
    }
    

    And I got what you show in the udemy screenshot.

    Little Hint to finish: I just noticed in your code that with Vue3, you declare the emit event, so you have to remove the '$' => @click="emit('close')"