Использование Modal из безголового пользовательского интерфейса в vue3

Я хочу использовать модальный интерфейс без заголовка в vue3, но проблема в том, что я не понимаю, как я могу переключить модальное окно с родительского (App.vue), если модальное окно является компонентом.

Я попытался передать опору в Modal.vue, но она не работает. Моя стратегия заключалась в том, чтобы использовать опору modalActive, посмотреть ее и вызвать соответствующую функцию для переключения модального окна, но она просто не работает.

В примере с пользовательским интерфейсом без головы используется кнопка, но она находится внутри самого компонента, поэтому очевидно, что он может без проблем получить доступ к функциям этого компонента.

Мой код:

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>

3 ответа

Вы должны использоватьвместе с $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>

Ссылка шаблона на ваш компонент:

        <Modal ref="modal">

И метод openModalв приложении Vue:

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

Вот рабочая версия: Vue SFC Playground

Вот важные части документации Vue 3 для понимания решения:

Ответ Tolbxela отлично работает, но если вы используете составной API, заключительная часть ответа будет выглядеть немного иначе.

      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()
}

Спасибо за эти ответы, ребята - у меня была та же проблема (еще не специалист по композиции Vue, так как я только что пришел из Vue 2.x).

Если кому-то интересно, я закрыл свой диалог только при нажатии кнопки «закрыть» в самом диалоговом окне (а не клавиши esc или щелчка за пределами диалогового окна). Это делает модальное окно действительно «модальным».

      <script setup>
  import { ref, watch, onMounted, onUnmounted } from 'vue'
  import {
    TransitionRoot,
    TransitionChild,
    Dialog,
    DialogPanel,
    DialogTitle,
  } from '@headlessui/vue'

  const isOpen = ref(false)
  
  function openModal(myEvent) {
    isOpen.value = true
  }

  function closeModal(ev) {
    if (typeof ev.target === 'undefined') return
    if (ev.target.id !== 'closeDialogBtn') return   // Give the 'close' button an id.
    isOpen.value = false
  }

  onMounted(() => {
    // block esc key from closing dialog.
    document.addEventListener('keydown', makeDialogModal)
  })

  onUnmounted(() => {
    // block esc key from closing dialog.
    document.removeEventListener('keydown', makeDialogModal)
  })

  function makeDialogModal(e) {
    if (e.key === 'Escape' || e.keyCode === 27) { // e.keyCode === 27 used for legacy
          console.log('Escape key pressed')
          e.stopPropagation()
          return
      }
  }

  defineExpose({
    openModal,
    closeModal
  })


</script>
Другие вопросы по тегам