Vue transition on static element not working - vue.js

I am trying to apply a very simple transition to div within in a Vue component. The element is not dependent on state or anything -- it just needs to slide in from the right when the component displays. For some reason, the transition I've set up is not working.
The component appears on my page as:
<MenuSideBar v-if="displayMenu" slide-right :items="menuItems />
And within that component, I have:
<template>
<transition name="slide">
<div v-if="slideRight" class="menu-container">
<div class="menu-inner">
<Menu :items="items />
</div>
</div>
</transition>
</template>
In my CSS for the transitions, I have:
.menu-container {
left: 0;
position: absolute;
top: 0;
width: 25vw;
}
.slide-enter{
right: -1000px;
}
.slide-enter-active {
transition: right 0.5s ease;
}
.slide-enter-to{
right: 0;
}
But when this component is displayed, there is no sliding in transition on that element.
Can anyone offer any advice on what I'm doing wrong?

For the sake of completeness, I'm posting my comment as the answer.
It seems like you want the transition to happen when the element is first rendered. To achieve that, you will need to add the appear attribute:
<template>
<transition name="slide" appear>
<div v-if="slideRight" class="menu-container">
<div class="menu-inner">
<Menu :items="items />
</div>
</div>
</transition>
</template>

Related

How to conditionally render multiple components based on route without affecting layout in vue3

I have a navbar and two side menus which I use in multiple pages. But for a number of pages I don't want to have the side menu.
I have tried using v-if with $router.name, but because I was using a grid system, the layout would break. Then also it was impossible to add more than one component to not render the side menus.
This is the template:
<template>
<TheNavbar/>
<div class="container">
<TheLeftMenu/>
<router-view/>
<TheRightMenu/>
</div>
</template>
My solution to avoid the grid system to squash the router-view component was to divide the template in two, one using the sidemenus, the other without them.
Then in order to have multiple pages to not load the side menus I used a computed property to hold an array with all the routes to not render the side menus:
<template>
<div v-if="blockedRoutes.includes($route.name) == false">
<TheNavbar/>
<div class="container">
<TheLeftMenu/>
<router-view/>
<TheRightMenu/>
</div>
</div>
<div v-else>
<TheNavbar/>
<router-view/>
</div>
</template>
computed: {
blockedRoutes () {
return ['Register', 'SignIn', 'About']
}
}
you can use the simple method of a flex container;
<template>
<router-view v-slot="{ Component, route }">
<div class="holder">
<div>
<TheLeftMenu/>
</div">
<div class="main">
<transition name="fade" mode="out-in"><component :is="Component" /></transition>
</div>
<div v-if="!route.meta.hideRmenu">
<TheRightMenu/>
</div>
</div>
</router-view>
</template>
<style>
.holder{
display : flex;
width : 100%;
/*if you want your container not to grow with the page do the following*/
overflow: hidden;
height : 100vh;
}
.holder .main{
flex : 1;
flex-direction: row;
/*if you want your container not to grow with the page do the following*/
max-height : 100vh;
overflow : auto;
}
.holder :not(.main){
min-width: 32px;
}
</style>
This way you can, if you want, also add transitions.
If you don't want to use transitions, you just need to remove the transitions tags.
This aproach uses is focused for Vue 3 and uses Scoped Slots

Vue transition works for "enter" state but not for "leave" state

I have a modal rendered on top of a semi-transparent backdrop. Both elements have a v-if controlled by the same variable.
Although the enter transition animation works fine, the `leave`` transition animation is ignored (it should fade out smoothly, instead it disappears instantly). Why?
Codepen
Markup:
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</div>
CSS:
.content-enter-active {
animation: slide-up .75s;
}
.content-leave-active {
animation: fade-out .75s;
}
#keyframes slide-up {
0% {
transform: translateY(100%);
}
100% {
transform: translateY(0);
}
}
#keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
It seems that the div with modalBackdrop class is disappearing before the nested div with class modalContent does its transition, so try to wrap modal Backdrop by a transition component with name backdrop which also takes the fade-out animation :
.backdrop-leave-active,.content-leave-active { /*.backdrop-leave-active is sufficient since the parent opacity is applied on children*/
animation: fade-out .75s;
}
template :
<div id="app">
<button #click="showModal = !showModal">Toggle Modal</button>
<transition name="backdrop" appear>
<div v-if="showModal" class="modalBackdrop">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>
</transition>
</div>
DEMO
When showModal is false, the transition element is destroyed immediately. If the only reason you use v-if="showModal" in transition parent is to disable modalBackdrop, then you can assign this class dynamically.
This is working as expected:
<div :class="{ modalBackdrop: showModal }">
<transition name="content" appear>
<div v-if="showModal" class="modalContent">
Modal
</div>
</transition>
</div>

How to make modal popup window in Vuejs?

<style src="./LoginButton.scss" lang="scss"></style>
<i18n src="./LoginButton.txt"></i18n>
<script src="./LoginButton.js"></script>
<template>
<div class="header-login component-same ml-10">
<span v-if="showLoggedIn">
<router-link :to="{ name: 'user' }" data-test="login-info-name">
<i class="dl-icon-user12"></i>
<span class="target-text hidden-xs hidden-sm">{{
$t('myAccount')
}}</span>
</router-link>
</span>
<!-- <span v-if="showLoggedIn" class="hidden-xs">
<a #click="logout" data-test="logout-button">
<span>{{ $t("signOut") }}</span>
</a>
</span>-->
<span v-else data-test="login-button">
<router-link :to="{ name: 'login' }">
<i class="dl-icon-user12"></i>
<span class="target-text hidden-xs hidden-sm">{{ $t('Register') }}</span>
</router-link>
</span>
</div>
</template>
I have written the above code for login, Where in this particular line
User has a register button, As soon as the user clicks on the register button, the User will be redirected to another page where he can fill up the basic details, So Instead of redirecting into another page, I want the Register button as a Popup, wherein the popup user can enter details.
also, I tried bootstarp-vue but I wasn't able to find the solution.
It's easier than what you think and mostly you won't need a special CSS lib for that, so the thing to do is just to make another component and call it whatever you want(but modal.vue makes sense here):
//inside modal.vue:
<template>
<div className="modal__wrapper">
<div className="modal">
<slot/>
</div>
</div>
</template>
<style lang="scss" scoped>
.modal{
width: 300px;
height: 100px;
border-radius: 10px;
background-color: white;
padding: 20px 20px;
z-index: 1200;
&__wrapper{
background-color: rgba(0,0,0,0.4);
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
position: absolute;
top: o;
}
}
</style>
in its simplest form, it's just a component with some styling to make it look like a modal (a transparent background(to make it look it's over the previous page) and an absolute position for its wrapper(the transparent one) and some centering techniques for its actual content),I didn't put any script in it(because it's just a simple modal I'm showing you to give the idea how to make yours) and then there is this slot part meaning you can pass your registration form to this modal component, from your parent component and use a boolean flag to just show it on register click
(sure you might need to add some more CSS to the .modal class):
<i18n src="./LoginButton.txt"></i18n>
<script src="./LoginButton.js"></script> <!-- the cleaner thing would
be to import them in your script section somehow -->
<template>
<div class="header-login component-same ml-10">
<span v-if="showLoggedIn">
<router-link :to="{ name: 'user' }" data-test="login-info-name">
<i class="dl-icon-user12"></i>
<span class="target-text hidden-xs hidden-sm">{{
$t('myAccount')
}}</span>
</router-link>
</span>
<span v-else data-test="login-button">
<!-- no need to rerouting to another path -->
<Modal>
<!--your template for registration will go here-->
<i class="dl-icon-user12"></i>
<span class="target-text hidden-xs hidden-sm">{{ $t('Register') }}</span>
</Modal>
</span>
</div>
</template>
//you gotta import the modal component so in your script section you have sth like this(BTW what type of structure are you using for your project? are you using vue-cli's scaffold or what? Are you using babel? cuz I'm gonna assume you are!)
<script>
import Modal from 'The_Relative_Path_to_the_Modal_you_just_created';
export default {
components:{
Modal
},
data(){
return{
showLoggedIn: false
}
}
}
</script>
<style src="./LoginButton.scss" lang="scss"></style>

Paper-badge appears in wrong position when used with app-layout

I'm using app-layout with app-drawer / app-drawer-layout and app-header / app-header-layout (see MWE below). The paper-badge is attached to an element inside the app-header. The first time the page is loaded, it briefly appears in the correct position (issue #12), than disappears off the right of the screen, exactly 256px too far to the right, which is the exact size of the app-drawer.
When the screen is resized, it recalculates the position to the correct place. I suspect that the position is calculated before the drawer is fully loaded.
This does not happen when the app-drawer is retracted and not visible.
I tried running updatePosition() at ready but that did not help.
Here is my MWE:
<template>
<style is="custom-style">
app-drawer {
--app-drawer-content-container: {
background-color: green;
};
}
app-header {
background-color: yellow;
}
</style>
<app-drawer-layout fullbleed>
<app-drawer slot="drawer">
<div id="drawerbox">Navigation Drawer</div>
</app-drawer>
<div class="container">
<app-header-layout>
<app-header id="header" condenses reveals effects="waterfall" slot="header">
<app-toolbar id="toolbarheader">
<div main-title id="toolbartitlebox">Title Toolbar</div>
<paper-icon-button id="discoursebutton" icon="communication:forum"></paper-icon-button>
<paper-badge for="discoursebutton" label="20" title="20 results"></paper-badge>
</app-toolbar>
</app-header>
</app-header-layout>
</div>
</app-drawer-layout>
</template>
One hack that works is to set a setTimeouton the updatePosition():
ready: function() {
var self = this;
setTimeout(function(){
self.$$('paper-badge').updatePosition();
}, 100);
}
But is there a more elegant way of solving this?
Thanks for your help!
One solution is to put paper-badge outside/after app-drawer-layout :
<template>
<style>
app-drawer {
--app-drawer-content-container: {
background-color: green;
}
;
}
app-header {
background-color: yellow;
}
paper-badge {
margin-top: 10px;
}
</style>
<app-drawer-layout fullbleed>
<app-drawer id="drawer" slot="drawer">
<div id="drawerbox">Navigation Drawer</div>
</app-drawer>
<app-header-layout>
<app-header id="header" condenses reveals effects="waterfall" slot="header">
<app-toolbar id="toolbarheader">
<paper-icon-button id="toggle" on-tap="_onToggle" icon="menu"></paper-icon-button>
<div main-title id="toolbartitlebox">Title Toolbar</div>
<paper-icon-button id="discoursebutton" icon="inbox"></paper-icon-button>
<!-- <paper-badge for="discoursebutton" label="20" title="20 results"></paper-badge>-->
</app-toolbar>
</app-header>
</app-header-layout>
<div class="container">
container
</div>
</app-drawer-layout>
<paper-badge for="discoursebutton" id="pd" label="20" title="20 results"></paper-badge>
</template>
Plnkr: http://plnkr.co/edit/V7zGBpqqzRonbAYyudea?p=preview
I avoided this same issue by putting the <paper-icon-button> and <paper-badge> in a container with position: relative, and stopped using the for attribute.
<div style="position: relative">
<paper-icon-button id="notification-icon" icon="social:notifications" onclick="[[showNotifications]]">
</paper-icon-button>
<template is="dom-if" if="[[messages.length]]">
<paper-badge label="[[messages.length]]" class="red"></paper-badge>
</template>
</div>

Vue 2 Transition not working

I have no idea where my code went wrong. It should be a simple transition. When I click the button the message shows correctly, but just that the there is no fade transition happening at all.
<template>
<div>
<transition name="fade">
<message v-show="showMessage" class="tr pop-up-message">
<p slot="header">This is Header</p>
<span slot="body">This is Body</span>
</message>
</transition>
<div v-if="!showMessage" class="block" #click.prevent="showMessage = true">
<a class="button is-primary">Primary</a>
</div>
<div v-else-if="showMessage" class="block" #click.prevent="showMessage = false">
<a class="button is-primary">Primary</a>
</div>
</div>
</template>
<script>
import message from './Message.vue'
export default {
components:{
'message': message,
},
data(){
return{
showMessage: false
}
},
}
</script>
Have you added these CSS as well:
.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0
}
I have tried to reproduce your code here with above CSS which works.
I had two <p> tags right next to each other like below
<p v-if="!isControlling">Take control of the camera by clicking</p>
<p v-else>Press <kbd>Esc</kbd> to exit camera. <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> <kbd>Space</kbd> <kbd>Shift</kbd> to move. Mouse to look.</p>
and they just didn't work. It looked like Vue was reusing the same <p> tag in the markup to render both. Adding a key to each made the transition work (thanks #Mark). The fix should look something like below
<p key="asd" v-if="!isControlling">Take control of the camera by clicking</p>
<p key="asf" v-else>Press <kbd>Esc</kbd> to exit camera. <kbd>W</kbd> <kbd>A</kbd> <kbd>S</kbd> <kbd>D</kbd> <kbd>Space</kbd> <kbd>Shift</kbd> to move. Mouse to look.</p>