How to control the visibility of a child component from the parent using a prop?

Viewed 890

I'm trying to control the visibility of a child component from the parent using a prop.

My child component has a visible prop. To avoid mutating the visible prop I assigned it to a local isVisible ref, and I'm using that to conditionally show and hide a form.

But when I try to hide the form using a button with @click="isVisible = false" nothing happens, and I get a console error saying Set operation on key "visible" failed: target is readonly.

It's also confusing why the error messsage is referring to the visible prop because I am using the local isVisible variable instead.

This is the ChildForm.vue (child component):

<template>
  <q-form v-if="isVisible">
    <q-input v-model="modelValue" />
    <q-btn label="Hide The Form" @click="isVisible = false" />
  </q-form>
</template>

<script setup lang="ts">
import { ref, toRef } from 'vue';
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false,
  },
});
const isVisible = toRef(props, 'visible');
const modelValue = ref('');
</script>

This is the ParentPage.vue (parent component):

<template>
  <child-form :visible="showForm" />
  <q-btn label="Show the Form" @click="showForm = true" />
</template>

<script setup lang="ts">
import { ref } from 'vue';
import ChildForm from 'src/components/ChildForm.vue';
const showForm = ref(false);
</script>
1 Answers

toRef() retains the reactive connection to the original source, so modifying isVisible effectively modifies the original prop, which is supposed to be readonly, leading to the warning you observed.

But I think you're actually trying to keep the child's visible prop in sync with the parent's showForm prop, such that updates in the child are automatically reflected in the parent. v-model is the tool for that problem.

In the parent, bind showForm to v-model:visible:

<child-form v-model:visible="showForm">

In the child, emit an update:visible event with the desired value whenever you want to update the parent:

<template>
  <q-form v-if="visible">
    <q-input v-model="modelValue" />                  
    <q-btn label="Hide The Form" @click="$emit('update:visible', false)" />
  </q-form>
</template>

<script setup lang="ts">
import { ref } from 'vue'
const props = defineProps({
  visible: {
    type: Boolean,
    required: true,
    default: false,
  },
})
const modelValue = ref('')
</script>

demo

Related