Parent has a button. When button is clicked how can we let the Child component know to open the modal?
Parent.vue
<script setup>
import Child from "Child.vue";
</script>
<template>
<button onClick=""> // open modal when button clicked
<Child/>
</template>
Child.vue
<script setup>
import { ref } from "vue";
const openModal = ref(false);
</script>
<template>
<div>example</div>
</template>
In the Child component, you can use defineExpose
to send the status
reactive variable accessible to the parent.
defineExpose()
- Vue 3 DocsdefineExpose
from component's <script setup>
not working in Vue 3 - StackOverflowor live CDN example:
const { createApp, ref, defineExpose } = Vue;
// Child
const ChildComponent = {
template: `
<div v-if="status">
<div>
<p>Modal is open!</p>
<button @click="closeModal">Close</button>
</div>
</div>
`,
setup() {
const status = ref(false);
const closeModal = () => {
status.value = false;
};
defineExpose({ status });
return { status, closeModal };
}
};
// Parent
const App = {
components: { ChildComponent },
setup() {
const childRef = ref(null);
const openChildModal = () => {
if (childRef.value) {
childRef.value.status = true;
}
};
return { childRef, openChildModal };
}
};
createApp(App).mount("#app");
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
<button @click="openChildModal">Open Modal</button>
<child-component ref="childRef"></child-component>
</div>
Nevertheless, I believe it is bad practice to extract a function from the component, as in the given example. A component should be self-contained, fully reusable at any time, and minimally affected by external factors. Therefore, I would place both the open and close buttons inside the child component, allowing the parent component to be informed about their clicks through emits.
defineEmits()
- Vue 3 Docs@emit
is not picked up by parent - StackOverflowYou can call the emit from the child component, and when it is triggered, the parent component will be notified, allowing you to build custom functions in response.
or live CDN example:
const { createApp, ref, defineEmits } = Vue;
// Child
const ChildComponent = {
template: `
<button v-if="! status" @click="openModal">Open Modal</button>
<div v-if="status">
<div>
<p>Modal is open!</p>
<button @click="closeModal">Close</button>
</div>
</div>
`,
setup(_, { emit }) {
const status = ref(false);
const openModal = () => {
status.value = true;
emit('opened');
};
const closeModal = () => {
status.value = false;
emit('closed');
};
return { status, openModal, closeModal };
}
};
// Parent
const App = {
components: { ChildComponent },
setup() {
const customFnForChildOpen = () => {
console.log('modal opened');
};
const customFnForChildClose = () => {
console.log('modal closed');
};
return { customFnForChildOpen, customFnForChildClose };
}
};
createApp(App).mount("#app");
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<div id="app">
<child-component @opened="customFnForChildOpen" @closed="customFnForChildClose"></child-component>
</div>