Using Vue's transition mode alongside v-for and v-if does not appear to function correctly

When using Vue with Vuetify, I am trying to dynamically change v-cards with the help of animate.css. However, I have run into an issue. The out-in mode is not working in this scenario as both fade-in and fade-out animations occur simultaneously. Is there a way to ensure that the fade-in animation starts only after the fade-out animation has ended?

new Vue({
  el: '#app',
  data() {
    return {
      number: 1,
      items: [
        {
          text: "a",
          number: 1
        },
        {
          text: "b",
          number: 2
        },
        {
          text: "c",
          number: 3
        },
      ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script><script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/animate.css" rel="stylesheet"/>

<div id="app">
  <div v-for="(item, index) in items" :key="index">
    <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
      <v-card dark class="ma-3" v-if="number === item.number">
        <p>{{item.text}}</p>
      </v-card>
    </transition>
  </div>
  <br>
  <v-btn @click="number++; if(number === 4) number = 1;">Next</v-btn>
</div>

https://codepen.io/km2442/pen/zgmmwz

Answer №1

What's the Issue?

The problem is that your transition is within a loop, which means after rendering you will have multiple transitions that are not related to each other (the mode won't work). This is how your template will appear after rendering:

<div key="0">
    <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
      <v-card dark class="ma-3" v-if="true">
        <p>a</p>
      </v-card>
    </transition>
 </div>
 <div key="1">
    <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
     <!-- <v-card dark class="ma-3" v-if="false">
        <p>b</p>
      </v-card> -->
    </transition>
 </div>
 <div key="2">
    <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
      <!-- <v-card dark class="ma-3" v-if="false">
        <p>c</p>
      </v-card> --> 
    </transition>
 </div>
 <div key="3">
    <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
      <!-- <v-card dark class="ma-3" v-if="false">
        <p>d</p>
      </v-card> -->
    </transition>
 </div>

Thus, when you click "Next," you are transitioning between different elements without the correct mode.

How Can I Resolve this?

To fix this issue, simply wrap all your items inside a single transition so that there is only one transition detecting when an element exits and another enters (the mode will then work).

Vue.config.devtools = false;
Vue.config.productionTip = false;

new Vue({
  el: '#app',
  data() {
    return {
      number: 1,
      items: [{
          text: "a",
          number: 1
        },
        {
          text: "b",
          number: 2
        },
        {
          text: "c",
          number: 3
        },
      ]
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/animate.css" rel="stylesheet" />

<div id="app">
  <transition mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
    <template v-for="(item, index) in items">
      <v-card dark class="ma-3" v-if="number === item.number" :key="index">
        <p>{{item.text}}</p>
      </v-card>
  </template>
  </transition>
  <br>
  <v-btn @click="number++; if(number === 4) number = 1;">Next</v-btn>
</div>

Answer №2

For iterating through a list using a v-for loop, remember to utilize the <transition-group>.

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"> 
</script><script src="https://unpkg.com/vuetify/dist/vuetify.min.js"></script>
<link href="https://unpkg.com/vuetify/dist/vuetify.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/npm/animate.css" rel="stylesheet"/>

<div id="app">
 <transition-group mode="out-in" enter-active-class="animated slideInLeft" leave-active-class="animated slideOutRight">
  <div v-for="(item, index) in items" :key="index">
    <v-card dark class="ma-3" v-if="number === item.number">
    <p>{{item.text}}</p>
  </v-card>
 </transition-group>
</div>
<br>
<v-btn @click="number++; if(number === 4) number = 1;">Next</v-btn>
</div>

Check out the documentation for more details: https://v2.vuejs.org/v2/guide/transitions.html#List-Transitions

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using NicEdit for uploading to your personal server

I'm having trouble uploading images using the NicEdit WYSIWYG editor on my own server. When I click on the upload image button, it redirects me to another site where I can upload the image: imgur dot com You can find a demo of this here: However, I ...

What is causing this code to malfunction?

Currently delving into the world of Ajax, I've been following a tutorial and have crafted the script below: <!DOCTYPE html> <html> <head> <script type="text/javascript"> function MyFunction(){ var xmlhttp; if(windo ...

What is the best way to mix up the middle letters within certain positions of a word?

Here is what I have managed to achieve so far: mounted() { const randomVariants = [...Array(3)].map(() => this.baseWord .split('') .sort(() => 0.5 - Math.random()) .join('') ) const variantsWithoutIniti ...

Ways to redirect to a different page following a successful execution of a mutation in React-query

I am facing an issue where a memory leak warning appears when I redirect to another page after a mutation. Despite trying various methods, I have not been able to find a solution. The specific warning message is: Warning: Can't perform a React state ...

Take away limitations from JavaScript code

I stumbled upon this code snippet online and I'm attempting to eliminate the Name must be letters only and min 3 and max 20 restriction. The name provided can have less than 3 characters and may include numbers. My JavaScript skills are still in the l ...

Strange behavior observed in the Datepicker, possibly linked to the blur event

I'm facing a challenge with the Datepicker feature. When I blur, the calendar disappears if the Datepicker was active, but the focus remains on the input field. As a result, I am unable to trigger the Datepicker again by clicking on the input field. T ...

Delay the execution of @mouseover in Vue: a guide to managing scope

Looking to implement an action only when the user has hovered over a div for at least 1 second. Here's how it's set up: <div @mouseover="trigger"></div> In the script section: data() { return { hovered: false } } m ...

Is there a Google Maps feature that displays clusters in a dropdown

Currently, I am utilizing Google Maps to place pins all over the world and implementing markercluster.js to cluster those pins when they are nearby. One feature I am trying to incorporate is the ability to hover over a cluster of pins and have a dropdown d ...

Receiving an error message and identifying the specific line number within the catch

try{ cause error } catch(err){ console.log(err.lineNumber) //or console.log(err.line) } The error object has various properties like err.name, err.stack, and err.message, but I have been unable to find a way to log the line number of the error ...

Guide on integrating web api on android with javascript, jquery, and jsonp

I am attempting to create a button in my Android application that, when clicked, retrieves information from a web API and displays the results on my screen. Here is the code that performs the necessary tasks, but I need to integrate it into my Android pag ...

Ensuring the value of a v-text-field in Vuetify using Cypress

I am currently developing an end-to-end test suite using Cypress for my Vue and Vuetify frontend framework. My goal is to evaluate the value of a read-only v-text-field, which displays a computed property based on user input. The implementation of my v-tex ...

Oops! Looks like there's a hiccup with the express-validator plugin as the validation fails due to req.checkBody not being recognized as

Currently, I am setting up a post route to handle a submit request. The code snippet provided is from my user.js route: var express = require('express'); var router = express.Router(); var multer = require('multer'); var upload = multe ...

ngResource transformResponse guide on dealing with both successful and erroneous responses

When my API, built using expressJS, returns JSON data as a regular response, everything functions smoothly. However, when an error occurs, it returns an error code along with plain text by simply invoking res.sendStatus(401). This poses a challenge on my ...

Unsynchronized Request Sequencing

Seeking advice on enhancing a previously accepted answer related to chained ajax requests can lead to some interesting solutions. One proposed solution involves sequencing 3 ajax requests in an asynchronous chain: var step_3 = function() { c.finish() ...

Node.js npm-migration enables the creation of multiple tables in a single migration process

I am new to npm-migration for nodejs and I am exploring ways to streamline the process of creating multiple tables using a single migration, rather than having separate migrations for each table creation. I have experimented with the following code: "up": ...

Exploring the differences between a callback function and an asynchronous

In the code snippet below, I have implemented handling for SIGINT and SIGTERM signals in a node.js application. // quit on ctrl+c when running docker in terminal process.on('SIGINT', async () => { console.info('Got SIGINT (aka ctrl+c in ...

How can I add a new property to an object type within an Interface in TypeScript?

I'm currently exploring how to merge declare an interface, with the twist of adding a property to the object literal type instead of directly to the interface itself. Within a library, I have a type that looks like this: interface DefaultSession { ...

Angular2: Learn how to dynamically create input fields when a button is clicked

My current challenge involves replicating input fields on click of a button. I have a set of input fields where data can be entered, and then I need to add another set of the same fields for additional data. There needs to be a way to remove these replicat ...

Creating a user-friendly form in a Nuxt/Vue application that enables dynamic attribute creation

I am currently working on designing a form that enables users to create different variations for a product within a Nuxt/Vue application. The goal is to provide users with the ability to specify attributes for each variation using a text field. These attr ...

A step-by-step guide on making a web API request to propublica.org using an Angular service

Currently, I am attempting to extract data from propublica.org's congress api using an Angular 8 service. Despite being new to making Http calls to an external web api, I am facing challenges in comprehending the documentation available at this link: ...