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 class="wl">
<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 class="card" :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="/" class="backLink">
<img src="@/assets/arrow-back-outline.svg" alt="go back button" />
<button>Back</button>
</RouterLink>
<div class="country" :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>
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.
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 class="wl"> delete this -->
<component :is="Component"></component>
<!-- </div> -->
</template>
<template #fallback>Loading..</template>
</suspense>
</Transition>
.
.
.
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 class="wl"></div> container -->
<div class="wl">
<RouterLink to="/" class="backLink">
<img src="@/assets/arrow-back-outline.svg" alt="go back button" />
<button>Back</button>
</RouterLink>
<div class="country" :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 class="country">
, 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.