Reputation: 1377
The title explains pretty much everything. I've searched StackOverflow for a solution but neither of them helped.
An example using classic modal component:
Vue.component('modal', {
template: `
<transition name="modal">
<div class="modal-wrapper">
<div class="modal-dialog">
<slot></slot>
</div>
</div>
</transition>
`,
})
const app = new Vue({
el: '#app',
data: {
showModal: false,
},
})
/* transition */
.modal-enter-active,
.modal-leave-active {
transition: opacity .5s;
}
.modal-enter,
.modal-leave-to {
opacity: 0;
}
.modal-wrapper {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .25);
}
.modal-dialog {
max-width: 90%;
padding: 1em;
background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
<p><button @click="showModal = true">Show modal</button></p>
<modal v-if="showModal">
<h3>Hello world</h3>
<p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
<p><button @click="showModal = false">Close</button></p>
</modal>
</div>
(There are no errors in the console as well)
However, everything is ok when using v-show
. But I can't really use it instead of v-if in my project.
Vue.component('modal', {
template: `
<transition name="modal">
<div class="modal-wrapper">
<div class="modal-dialog">
<slot></slot>
</div>
</div>
</transition>
`,
})
const app = new Vue({
el: '#app',
data: {
showModal: false,
},
})
/* transition */
.modal-enter-active,
.modal-leave-active {
transition: opacity .5s;
}
.modal-enter,
.modal-leave-to {
opacity: 0;
}
.modal-wrapper {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .25);
}
.modal-dialog {
max-width: 90%;
padding: 1em;
background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
<p><button @click="showModal = true">Show modal</button></p>
<modal v-show="showModal">
<h3>Hello world</h3>
<p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
<p><button @click="showModal = false">Close</button></p>
</modal>
</div>
That said, I have to wrap <modal>
with <transition>
everywhere modal is used and remove the transition from the modal itself (which doesn't sound good)
<transition name="modal">
<modal>
...
</modal>
</transition>
Why is it so and How to make entering animation work (with v-if
, and <transition>
in the modal component?
I have noticed that there is no such problem with Vue 2.5 (instead of Vue 2.6). There surely something changed since then.
Upvotes: 2
Views: 12878
Reputation: 1377
This should do the trick:
{
template: `
<transition name="modal">
<div v-if="show">...</div>
</transition>
`,
data() {return {
show: false,
}},
mounted() {
this.show = true
},
}
This method in my opinion (and I believe it does) delays animation by 1 Vue tick, and nesting such animated element a lot would make a noticable slow down.
Vue.component('modal', {
template: `
<transition name="modal">
<div class="modal-wrapper" v-if="show">
<div class="modal-dialog">
<slot></slot>
</div>
</div>
</transition>
`,
data() {return {
show: false,
}},
mounted() {
this.show = true
}
})
const app = new Vue({
el: '#app',
data: {
showModal: false,
},
})
/* transition */
.modal-enter-active,
.modal-leave-active {
transition: opacity .5s;
}
.modal-enter,
.modal-leave-to {
opacity: 0;
}
.modal-wrapper {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .25);
}
.modal-dialog {
max-width: 90%;
padding: 1em;
background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
<p><button @click="showModal = true">Show modal</button></p>
<modal v-if="showModal">
<h3>Hello world</h3>
<p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
<p><button @click="showModal = false">Close</button></p>
</modal>
</div>
Upvotes: -1
Reputation: 1377
You are missing the appear
attribute.
By default, Vue will not animate when element is first inserted. As per docs:
If you also want to apply a transition on the initial render of a node, you can add the appear attribute:
<transition appear> <!-- ... --> </transition>
By default, this will use the transitions specified for entering and leaving. If you’d like however, you can also specify custom CSS classes:
<transition appear appear-class="custom-appear-class" appear-to-class="custom-appear-to-class" appear-active-class="custom-appear-active-class" > <!-- ... --> </transition>
So just adding appear
should fix the problem.
Upvotes: 7
Reputation: 4766
That's because v-if inserts/destroys elements and v-show hides them (does not remove them from DOM). In your example, transition
does not exists if v-if
is setup on/above transition element. If you move v-if
below transition, then it will work.
Vue.component('modal', {
props: ['showModal'],
template: `
<transition name="modal">
<div class="modal-wrapper" v-if="showModal">
<div class="modal-dialog">
<slot></slot>
</div>
</div>
</transition>
`,
})
const app = new Vue({
el: '#app',
data: {
showModal: false,
},
})
/* transition */
.modal-enter-active,
.modal-leave-active {
transition: opacity .5s;
}
.modal-enter,
.modal-leave-to {
opacity: 0;
}
.modal-wrapper {
position: absolute;
left: 0; right: 0;
top: 0; bottom: 0;
display: flex;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, .25);
}
.modal-dialog {
max-width: 90%;
padding: 1em;
background: white;
}
<script src="https://vuejs.org/js/vue.js"></script>
<div id="app">
<p><button @click="showModal = true">Show modal</button></p>
<modal :show-modal="showModal">
<h3>Hello world</h3>
<p>Amet quam alias amet incidunt voluptatum sapiente Mollitia</p>
<p><button @click="showModal = false">Close</button></p>
</modal>
</div>
Upvotes: 2