javascript - How to open modal located in child component from parent when button clicked? - Stack Overflow

admin2025-04-17  3

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>

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>
Share Improve this question edited Feb 1 at 8:42 rozsazoltan 11.3k6 gold badges21 silver badges59 bronze badges asked Feb 1 at 8:07 basebase 1117 bronze badges 1
  • Use props, this is exactly what they’re meant for - sharing state from parent to child – Sami Kuhmonen Commented Feb 1 at 8:13
Add a comment  | 

2 Answers 2

Reset to default 3

Sharing a variable with the parent

In the Child component, you can use defineExpose to send the status reactive variable accessible to the parent.

  • defineExpose() - Vue 3 Docs
  • defineExpose from component's <script setup> not working in Vue 3 - StackOverflow
Example
  • Try with Playground

or 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>

Notify the parent about an event

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 - StackOverflow
  • Vue Composition API: Defining emits - StackOverflow

You 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.

Example
  • Try with Playground

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>

An easy way to do this would be with props. If the parent has a ref isModalShown and the child takes this in as modelvalue, then the child can dynamically react to the state of that ref in the parent component, as well as mutate it directly. In script setup, it would look something like this:

In the parent:

<template>
  <button onclick="isModalShown = true">show</button>
  <Child v-model="isModalShown" />
</template>

<script setup>
  const isModalShown = ref(false);
</script>

and in the child:

<template>
  <MyModal v-model="isShown" />
</template>

<script setup>
  const isShown = defineModel('modelValue');

  function onClose() {
    isShown.value = false; // we can mutate the modelValue and the change will be emitted to the parent as an event, causing both components to update
  }
</script>

This is core functionality of the Vue framework, and things like defineEmits would probably be best reserved for cases where we need to invoke methods of a child component, not just update a ref's value to be in sync with the parent.

转载请注明原文地址:http://anycun.com/QandA/1744834765a88274.html