Here is my code snippet demonstrating how I created a modal using Vue. The issue I am encountering revolves around maintaining the transition effects while conditionally mounting the Vue component. Upon clicking the button in the initial code block, the modal opens and then 'mounted' is logged to the console. This behavior aligns with my expectations as the modal smoothly fades in due to the appear attribute.
However, the fade out effect does not occur because of the v-if statement. I aim to sustain the transition functionalities while retaining the v-if condition, which delays the mounted and unmounted hooks.
The necessity for keeping the v-if="showModal" stems from the challenges on iOS related to modals. To counter this, I intend to set the body to position: fixed and adjust the top property within the modal components during the onMounted hook. Removing v-if="showModal" results in the modal component being mounted upon page load, which contradicts my intentions.
<script setup>
const showModal = ref(false)
</script>
<button @click="showModal = true">show modal</button>
<Teleport to=".vue-modal-target">
<Modal :show="showModal" @close="showModal = false" v-if="showModal">
<ContactForm />
</Modal>
</Teleport>
In the Modal.vue
file, the following code is implemented:
<script setup>
onMounted(() => {
console.log('mounted')
})
onUnmounted(() => {
console.log('unmounted')
})
</script>
<template>
<Transition appear name="modal">
<div v-if="show" class="modal-wrapper">
<div class="modal-mask"></div>
<div class="modal-container">
<button class="close-btn" @click="$emit('close')" style="color: #fff;">X</button>
<div class="modal-container__inner">
<slot />
</div>
</div>
</div>
</Transition>
</template>
<style>
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-to,
.modal-leave-from {
opacity: 1;
}
.modal-enter-active,
.modal-leave-active {
transition: all 0.5s ease-in-out;
}
</style>
EDIT
I have made adjustments based on the recommendation provided in the accepted answer. Here's the updated code:
<script setup>
const showModal = ref(false)
</script>
<button @click="showModal = true">show modal</button>
<Teleport to=".vue-modal-target">
<Modal :show="showModal" @close="showModal = false" v-if="showModal">
<ContactForm />
</Modal>
</Teleport>
Code snippet used in the updated Modal.vue
file:
<script setup>
onMounted(() => {
console.log('mounted')
})
onUnmounted(() => {
console.log('unmounted')
})
const props = defineProps({
show: Boolean
})
const showModal = ref(false)
if (props.show === true) showModal.value = true
const emit = defineEmits(['close'])
function handleClose() {
showModal.value = false
setTimeout(() => {
emit('close')
}, 500)
}
</script>
<template>
<Transition appear name="modal">
<div v-if="showModal" class="modal-wrapper">
<div class="modal-mask"></div>
<div class="modal-container">
<button class="close-btn" @click="handleClose" style="color: #fff;">X</button>
<div class="modal-container__inner">
<slot />
</div>
</div>
</div>
</Transition>
</template>
<style>
.modal-enter-from,
.modal-leave-to {
opacity: 0;
}
.modal-enter-to,
.modal-leave-from {
opacity: 1;
}
.modal-enter-active,
.modal-leave-active {
transition: all 0.5s ease-in-out;
}
</style>