Vuejs transition 1 pager for components - vuejs2

I have 1 page with 2 components. Component A has a button, when clicking disalbe A and show component B.
If i use transition i am getting this error.
I want a nice fadeout A and fade in B, how can i achieve?
Error:
[Vue warn]: <transition> can only be used on a single element. Use <transition-group> for lists.
App.js
<transition name="fade">
<div class="fade-enter-active" v-show="datatable">
<component-a :title="'AAA'"></component-a>
<button v-on:click="showCompB">Show B and disable A</button>
</div>
<div class="fade-enter-active" v-show="componentb">
<component-b :title="'BBB'"></component-b>
</div>
</transition>
export default {
data() {
return {
datatable: true,
componentb: false,
etc etc

Good morning sir,
As the error stated, the <transition> component can be used only with a single child element. You can learn more about that here: https://v2.vuejs.org/v2/guide/transitions.html
You could instead use two <transition> components to handle the fade animation for each one of your elements like so:
<transition name="fade">
<div v-show="datatable">
<component-a :title="'AAA'"></component-a>
<button v-on:click="showCompB">Show B and disable A</button>
</div>
</transition>
<transition name="fade">
<div v-show="componentb">
<component-b :title="'BBB'"></component-b>
</div>
</transition>
The fade animation will be applied to each div whenever componentb and datatable is visible or not.
Hope that helps you.

Related

Vue 3 – <Transition> renders non-element root node that cannot be animated

App.vue has a transition tag to fade the pages out and in.
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in" appear>
<component :is="Component"></component>
</transition>
</router-view>
The Page.vue file has a simple structure, however, it also has a basic sliderjs component which throws the error <Transition> renders non-element root node that cannot be animated. If the transition tag is removed, everything works fine.
<div v-if="page.isReady">
<swiper>
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper>
</div>
https://swiperjs.com/vue/
The file also has the following:
import { Swiper, SwiperSlide } from 'swiper/vue';
import 'swiper/swiper.scss';
export default {
components: {
Swiper,
SwiperSlide,
},
setup () {
return {
page: usePage()
}
}
}
Is there any trick to fix the error? Thanks for any tips!
No.
<template>
<div></div>
<div>~someone~</div>
</template>
Yes.
<template>
<div>
  <div></div>
~someone~
</div>
</template>
If you do not use a "div" tag just inside the "Template" tag, you will get the same error. (By the way, it was possible to use other than div tags)
Transitions require single children nodes. Therefore you can wrap the <component> tag inside a <div>, however, a plain <div> inside a <transition> won't trigger the transition, but changing the key attribute does.
We can obtain a unique key by getting the route name:
<router-view v-slot="{ Component, route }">
<transition name="fade" mode="out-in">
<div :key="route.name">
<component :is="Component"></component>
</div>
</transition>
</router-view>
This will effectively transition between routes with a different name, but if you also want to transition between routes of the same name with different parameters, you can use route.fullPath instead of route.name as the key.
I can't fully take credit for this one...but I was having a similar issue and the problem was I had multiple nodes in my view, and found this guy's post on the Vue.js forums:
Found my mistake too. Transition required a single root in components! Since Vue 3 no longer requires a single root node for components I thought this also applies to transitions.
But it’s also logical. CSS requires a single root to which the transitions can refer.
When toggling between elements that have the same tag name, you must tell Vue that they are distinct elements by giving them unique key attributes. Otherwise, Vue’s compiler will only replace the content of the element for efficiency. Even when technically unnecessary though, it’s considered good practice to always key multiple items within a component.
<div>
<router-link to="/"></router-link>
<router-link to="/about"></router-link>
</div>
<router-view v-slot="{ Component, route }">
<transition name="route" :key="route" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
I solved it, it was a minor mistake, there was a character outside a html tag, directly after the tag (comma).
<template>,
<div>
<div>
<swiper>
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper>
</div>
</div>
</template>
your dynamic component instance must have a root element.
in you example,'Swiper' and 'SwiperSlide' must have a root element!
don't use RouterView in component parameter of the router. if you need to do that put it inside a root element
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in" appear>
<!-- root element -->
<div>
<component :is="Component"></component>
</div>
</transition>
</router-view>
A solution that worked for me using Nuxt3:
Parent page was:
<template>
<NuxtPage />
</template>
but should be also wrapped into a root node:
<template>
<div>
<NuxtPage />
</div>
</template>
Otherwise I got a transition exception:
Component inside <Transition> renders non-element root node that cannot be animated
I'm doing like this and it works.
<template>
<router-view v-slot="slotProps">
<transition name="route" mode="out-in">
<component :is="slotProps.Component"></component>
</transition>
</router-view>
</template>

VueJS transition on computed values

I want to animate a block with posts that can be filtered.
Some filters trigger a computed method filteredPosts and they are assigned to a component liek that <block-article :posts="filteredPosts" />
In my <block-article> component I have something like that :
<template>
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</template>
I want div .posts animate like a translation bottom/fade out on disappear and translation top/ fade in on appear.
I tried that :
<template>
<transition name="slide-fade">
<div class="posts">
<div v-for="post in posts" :key="post.id"></div>
</div>
</transition>
</template>
with corresponding css classes but it doesn't work.
I tried that :
<template>
<div class="posts">
<transition-group name="slide-fade">
<div v-for="post in posts" :key="post.id"></div>
</transition-group>
</div>
</template>
but my class .posts is a grid and here I lost the grid behavior.
THE AIM is to animate the entire div .posts rather than each elements of the v-for.
Any idea ?
Thanks all,
I finally achieve this with :
<transition name="slide-fade">
<div :key="posts.length" class="posts"></div>
</transition>
Nothe the :key="posts.length"
The problem is when posts.length doesn't change but it works in a lots of case. I will search how to fix this exception.
If you animate entire div, you should use transition, but in this case all inner elements not animated. If you want to animate all inner elements. You should use transition-group
In your case I think, need use all this method with build-in tag attribute.
Becouse in dock you can read
https://v2.vuejs.org/v2/guide/transitions.html
Unlike transition, it renders an actual element: a span by default. You can change the element that’s rendered with the tag attribute.
So you can write like this(its not full code, you must add name, key and other attrs)
<transition>
<transition-group tag="div" class="posts">
<div v-for="post in posts"></div>
</transition-group>
</transition>

Vue transition works on router-view but not inside component

When I setup a typical route transition everything works as expected
<transition name="view" mode="out-in">
<router-view />
</transition>
But if I try to put the transition inside a view instead, the transition doesn't work
<template>
<transition name="view" mode="out-in">
<main>
<...>
</main>
</transition>
</template>
Any ideas why this could be the case?
The issue, seems to me, is that the <main> element is not entering or leaving, so it doesn't animate. You're probably trying to animate something within the <main> element which does not get targeted by the <transition> pseudo-element.
my suggestion is to encapsulate the element that's being toggled (v-if) or having visibility toggled (v-show) with transition
<template>
<main>
<transition name="view" mode="out-in">
<...something with a v-if or a v-show>
</transition>
</main>
</template>
Also, if you have are using a list/array (v-for) you should use transition-group
docs: https://v2.vuejs.org/v2/guide/transitions.html

Can't get Vue.js transition work without a warning message

I am using Vue.js transitions to fade elements in/out based on conditional rendering.
First, I am transitioning a group of components. This is working perfectly!
<div>
<transition-group name="component-fade" mode="out-in">
<component-one key="1" v-show="foo === 'one'" :type="type"/>
<component-one key="2" v-show="foo === 'two'" :type="type"/>
<component-one key="3" v-show="foo === 'three'" :type="type"/>
</transition-group>
</div>
Each component <component-one.../> is identical. I am rendering a bunch of <div> elements:
// component-one.vue
<template>
<div>
<div id="div-one">
<transition name="fade" mode="out-in">
<div key="one" v-if="foo === 'bar'">
<h3>My First Div</h3>
...
</div>
<div key="two" v-if="foo === 'bazz'">
<h3>My Second Div</h3>
...
</div>
<div key="three" v-if="foo === 'other'">
<h3>My Third Div</h3>
...
</div>
...
</transition>
</div>
</div>
</template>
The functionally works great. The components fade in/out nicely as to the div elements. However, I am getting a warning from vue:
[Vue warn]: can only be used on a single element. Use for lists.
This makes sense as I am transitioning a group of div elements. However, if I use <transition-group> the mode of out-in is no longer working. As I toggle through my div elements, they snap in/out of position as they are fading in/out. I have tried every combination -- including using v-if or v-show to see if that made a difference.
How can I use the transitions I have (that work) but not generate the warning?
`Adding "keys" on each item.
try this.
new Vue({
el: "#app",
data: {
foo: "bar"
},
methods: {
}
})
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="foo = 'bar'">show bar</button>
<button #click="foo = 'bazz'">show bazz</button>
<button #click="foo = 'other'">show other</button>
<transition name="fade" mode="out-in">
<div key="one" v-if="foo === 'bar'">
<h3>bar</h3>
</div>
<div key="two" v-if="foo === 'bazz'">
<h3>bazz</h3>
</div>
<div key="three" v-if="foo === 'other'">
<h3>other</h3>
</div>
</transition>
</div>
I believe that the transition element is supposed to wrap just one element and that the reason v-show isn't working is because v-show doesn't actually add/remove anything from the DOM, it just toggles the visibility attribute. If you use any logic that could theoretically show more than one at the same time, you'll get a warning. So what I think you'd really want is for your transition element to wrap each of the individual component-one elements. v-show should take care of the individual element's visibility. Basically, you need the final markup to look like this:
<transition name="fade" mode="out-in">
<div key="one" v-if="foo === 'bar'">
<h3>My First Div</h3>
...`enter code here`
</div>
</transition>
<transition name="fade" mode="out-in">
<div key="two" v-if="foo === 'bar'">
<h3>My Second Div</h3>
...
</div>
</transition>
Try doing either:
A v-for on the transition component that loops through a list of all the items you'd like to render. Each transition should have its own component-one child as it loops through the list.
<transition v-for="(item, index) in items" name="fade" mode="out-in">
<component-one :key="index" v-show="foo === item" :type="type"/>
</transition>
Wrapping the contents of component-one inside a transition. That would include the transition inside the component-one element. Then you can render component-one elements without any transition elements as a parent.
component-one
<transition name="fade" mode="out-in">
<div>
<!-- Component One contents... -->
</div>
</transition>
main
<div>
<component-one key="1" v-show="foo === 'one'" :type="type"/>
<component-one key="2" v-show="foo === 'two'" :type="type"/>
<component-one key="3" v-show="foo === 'three'" :type="type"/>
</div>
The above answers are in fact correct and should be considered by anyone who comes across this thread. I also wanted to add what I found was the underlying issue.
I was using <transition-group> on my parent element(s) that I was trying to transition between. Each component that I was transitioning to had multiple div's underneath. This is what was bubbling up (for lack of better words) that was looking for a <transition-group>.
TL;DR:
The error was coming from the child component(s) that needed to make use of <transition-group>.
Seems obvious now, but hope can save other folks some time.

Vue Transition inside the div element doesn't work

Could someone explain me, why the following code doesn't work?
<template>
<div class="modal">
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
<transition name="fade-in">
<div #click="close" class="modal__overlay"/>
</transition>
</div>
</template>
I'm trying to create modal with two different animations (slide-in for text area and fade-in for modal overlay).
If i delete the element with class modal and edit code to the following everything works fine.
<template>
<transition name="slide-in">
<div #click.stop class="modal__container">
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</template>
Referencing Vue.js documentation on transitions
Vue provides a variety of ways to apply transition effects when items are inserted, updated, or removed from the DOM
That means that DOM nodes that transition applies classes to, should be those which are inserted/updated/removed.
Since it is a modal window, I assume that it has v-if directive applied in parent component to handle it's visibility. In order for transition to work, it should wrap DOM element that will be updated.
You can understand it more easily if you move code of your modal window into the parent component. Just for better visualization of elements tree and transition's behavior.
In first example, conditional rendering (v-if) applies to <div class="modal">, which is not wrapped with transition - therefore no animation will be triggered. At the same time, nested nodes are wrapped with transition, but there is nothing that will update or remove them. They are statically displayed and inserted initially on component's creation. Nothing to animate.
In order for it to work as expected following structure would be advisable:
<template>
<transition name="fade-in">
<div
class="modal__overlay"
#click="close"
>
<transition name="slide-in">
<div
v-if="containerVisible"
class="modal__container"
#click.stop
>
<div #click="close" class="modal__button">
<span class="modal__button--close">Close</span>
</div>
</div>
</transition>
</div>
</transition>
</template>
This solution expects modal__overlay to have position: fixed; style and variable containerVisible to be set to true inside mounted hook of modal component.