Home > database >  Vue route transitions only works on load or refresh, but no animations on routing
Vue route transitions only works on load or refresh, but no animations on routing

Time:10-16

I added a transition on a suspense component as the Vue's docs suggested way of doing it to route to a dynamic route path, the animations works when loading and reloading each page individually but it doesn't work on routing (when users click on a routerlink), also there are no errors shown.

to see how this behaves you can take a look at the live site: https://rest-countries-api.onrender.com/

and to see all the code you can go to : https://github.com/anas-cd/rest_api/tree/dev/rest_api

here is the main App.vue file:

<template>
    <NavigationBar />
    <RouterView v-slot="{ Component }">
        <template v-if="Component">
            <Transition name="scale" mode="out-in">
                <suspense>
                    <template #default>
                        <div >
                            <component :is="Component"></component>
                        </div>
                    </template>
                    <template #fallback>Loading..</template>
                </suspense>
            </Transition>
        </template>
    </RouterView>
</template>

<script setup>
import NavigationBar from './components/NavigationBar.vue';
</script>

<style lang="scss">
// other styling 

// transition styling
.scale-enter-active,
.scale-leave-active {
    transition: all 0.5s ease;
}

.scale-enter-from,
.scale-leave-to {
    opacity: 0;
    transform: scale(0.8);
}
</style>

also here is a component for routing placed on the home page (simplified for context):

<template>
    <router-link :to="countryCode">
        <div  :key="$route.params.code">
                <h2>{{ countryName }}</h2>
        </div>
    </router-link>
</template>

<script setup>
// imports
import { ref } from 'vue';

// data
const props = defineProps({
    countryData: {
        type: Object,
        required: true,
    },
});

const countryName = ref(props.countryData.name.common);
</script>

<style scoped lang="scss">
// styling ....
</style>

lastly here is the destination view :

<template>
    <RouterLink to="/" >
        <img src="@/assets/arrow-back-outline.svg" alt="go back button" />
        <button>Back</button>
    </RouterLink>
    <div  :key="$route.params.code">
       name
    </div>
</template>

<script setup>
import { ref, watchEffect } from 'vue';
import { useRoute } from 'vue-router';
import CountriesApi from '@/services/CountriesApi';


const route = useRoute();
// data
const countryData = ref(
    await CountriesApi.getCountryByCode(route.params.code).catch(() => {
        return null;
    })
);

const name = ref('');

name.value = countryData.value[0].name.common;

watchEffect(async () => {
 
        countryData.value = await CountriesApi.getCountryByCode(
            route.params.code
        
        name.value = countryData.value[0].name.common;
    
});
</script>

CodePudding user response:

for future reference, after many tries, the problem was that <Transition> only accepted one node as a child in both origin and destination views/components to apply its transition animations on that root node.

so here are the required changes

1

App.vue: remove the <div> (or other HTML elements) wrapping container for <component>, and instead add it in your components/views acting as a root node wrapping everything there.

.
.
.
<Transition name="scale" mode="out-in">
  <suspense>
    <template #default>
       <!-- <div >  delete this -->
         <component :is="Component"></component>
       <!-- </div> --> 
      </template>
    <template #fallback>Loading..</template>
  </suspense>
</Transition>
.
.
.

2

all views and components: you need to add a container root node, <div> or whatever works with your app for <Transition> to apply its animation effects on.

<template>
<!-- adding the root node as <div ></div> container -->
   <div > 
     <RouterLink to="/" >
         <img src="@/assets/arrow-back-outline.svg" alt="go back button" />
         <button>Back</button>
     </RouterLink>
     <div  :key="$route.params.code">
        name
     </div>
   </div>
</template>

Simply it didn't work before because the destination view got two root elements <RouterLink> and <div >, thus <Transition> doesn't have a root node to apply the animations on.

this usually gives an error or warning but for some reason, it didn't in my case.

  • Related