1👍
Simplifying and correcting for mistakes, the TestComponent.vue should be like this:
<script setup lang="ts">
defineProps({
isVisible: {
type: Boolean,
default: true,
}
})
defineEmits<{'update:isVisible': [value: boolean]}>()
</script>
<template>
<div v-if="isVisible">
<button @click="$emit('update:isVisible', false)">Test</button>
</div>
</template>
You defined an emit, but never used it, so the value was never being sent back up to the parent. I put the emit on the button click, emitting the value of false. You had a typo with the prop name modalValue
, it should’ve been modelValue
, but since it looked like you wanted to use a name of isVisible
I just changed it to that instead and got rid of the ref.
Parent component:
<script setup lang="ts">
import TestComponent from '@/components/TestComponent.vue'
import { ref } from 'vue'
const test = ref(true)
</script>
<template>
<main>
{{ test }}
<TestComponent v-model:isVisible="test" />
</main>
</template>
Here you needed to use v-model instead of v-bind. v-bind is for one-way binding, v-model is two-way. To set the two-way binding where the child prop is called isVisible
, the v-model should be written as v-model:isVisible
.
Edit: Using v-bind
I now understand that the specific use case in the question is for an optional prop, so that the parent component and child component can both independently set the isVisible
value.
The strategy for this is to use a local variable and a watcher. If the parent changes the prop value, the watcher activates and changes the local variable. If no prop is provided, the child component can still update the local variable on it’s own.
Parent component
<script setup lang="ts">
import TestComponent from './TestComponent.vue'
import { ref } from 'vue'
const test = ref(true)
</script>
<template>
<main>
<TestComponent :isVisible="test" />
<TestComponent />
</main>
</template>
Child component
<script setup lang="ts">
import { ref, watch } from 'vue'
const props = defineProps({
isVisible: {
type: Boolean,
default: true,
required: false
}
})
const isActuallyVisible = ref(true)
watch(
() => props.isVisible,
(newValue) => {
isActuallyVisible.value = newValue
}
)
</script>
<template>
<div v-if="isActuallyVisible">
<button @click="isActuallyVisible = false">Test</button>
</div>
</template>