Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Modal from headless ui in vue3

I want to use headless ui modal in vue3 but the problem is I don't understand how I can toggle the modal from parent (App.vue) if the modal is a component.

I tried to pass a prop in the Modal.vue but it's not working, my strategy was to use a modalActive prop and watch it and call appropriate function to toggle the modal, but it's simply not working.

The example in headless UI uses a button but that's inside the component it self so obviously it can access functions of that component without problem.

My code:

App.vue

<template>
  <Modal>
    <template v-slot:default>
      <LoginForm />
    </template>
  </Modal>
</template>


<script setup lang="ts">

import Modal from './components/Modal.vue';
import LoginForm from './components/LoginForm.vue';
import { ref } from 'vue';

</script>

Modal.vue

<template>
    <TransitionRoot appear :show="isOpen" as="template">
        <Dialog as="div" @close="closeModal">
            <div class="fixed inset-0 z-10 overflow-y-auto">
                <div class="min-h-screen px-4 text-center">
                    <TransitionChild
                        as="template"
                        enter="duration-300 ease-out"
                        enter-from="opacity-0"
                        enter-to="opacity-100"
                        leave="duration-200 ease-in"
                        leave-from="opacity-100"
                        leave-to="opacity-0"
                    >
                        <DialogOverlay class="fixed inset-0" />
                    </TransitionChild>
                    <span class="inline-block h-screen align-middle" aria-hidden="true">&#8203;</span>
                    <TransitionChild
                        as="template"
                        enter="duration-300 ease-out"
                        enter-from="opacity-0 scale-95"
                        enter-to="opacity-100 scale-100"
                        leave="duration-200 ease-in"
                        leave-from="opacity-100 scale-100"
                        leave-to="opacity-0 scale-95"
                    >
                        <div
                            class="inline-block w-full max-w-md p-6 my-8 overflow-hidden text-left align-middle transition-all transform bg-white shadow-xl rounded-2xl"
                        >
                            <slot></slot> <!-- slot for forms -->
                        </div>
                    </TransitionChild>
                </div>
            </div>
        </Dialog>
    </TransitionRoot>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue'
import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogOverlay,
    DialogTitle,
} from '@headlessui/vue'

const isOpen = ref(true)


function closeModal() {
    isOpen.value = false

}
function openModal() {
    isOpen.value = true
}

</script>

like image 208
Veerendra Singh Avatar asked Oct 18 '25 11:10

Veerendra Singh


2 Answers

You should use defineExpose together with $refs.

<script setup>
import { ref, watch, defineExpose } from 'vue'

...      

const closeModal =  function() {
    isOpen.value = false
}
const openModal = function() {
    isOpen.value = true
}
  
defineExpose({
  openModal,
  closeModal
})
  
</script>

Template reference to your component:

  <Modal ref="modal">

And the method openModal in the Vue App:

components: {
      Modal
    },
    methods: {
      openModal() {
        this.$refs.modal.openModal();
      }
    }

Here is a working version: Vue SFC Playground

Here are the important parts of Vue 3 Documentation to understand the solution:

  • Template Refs
  • SFC <script setup>
like image 192
Tolbxela Avatar answered Oct 20 '25 00:10

Tolbxela


Tolbxela's answer works great, but if you're using the composition api, the final part of the answer will look a little different.

const modal = ref(null)
//the variable name (modal) needs to match the template ref name 
//given in the second step of Tolbxela's answer

const openModal = () => {
    modal.value.openModal()
}
like image 29
B Carrera Avatar answered Oct 20 '25 00:10

B Carrera



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!