Amini
Amini

Reputation: 1792

Unwanted animation bug in Vue.js

This is an unwanted animation bug that seems to be normal, but I want the element to be animationed which is in <input v-model="todo"/>. The problem is each time I press on add button to add the todo name (<input v-model="todo"/>), the last element 'last' runs the animation.

P.s. I tried to keep the code as simple as possible.

Vue.createApp({
  data() {
    return {
      todo: "",
      todo_list: ['last'],
    };
  },

  methods: {
    add() {
      this.todo_list.unshift(this.todo);
      console.log(this.todo_list);
    },
    remove(index) {
      this.todo_list.splice(index, 1);
    },
  },
}).mount('#app');
.list-enter-active,
.list-leave-active {
    transition: all 200ms ease-out;
}

.list-enter-from {
    transform: translateY(-20px);
}

.list-leave-to {
    opacity: 0;
    transform: translateX(20px);
}

article {
    width: 50%;
    padding: 10px;
    background-color: #dddddd;
    border: 1px solid #cfcfcf;
    border-radius: 5px;
    margin-block: 10px
}
<script src="https://unpkg.com/vue@next"></script>

<div id="app">
  <input type="text" v-model="todo" /> <button @click="add">add</button>
  <transition-group name="list" tag="section">
    <article v-for="(todo, index) in todo_list" :key="index" @click="remove(index)">
      <span>{{ todo }}</span>
    </article>
  </transition-group>
</div>

Upvotes: 2

Views: 409

Answers (1)

Hiws
Hiws

Reputation: 10334

The reason you're experiencing this issue, is because you're using the index as a key. If you added the elements to the end of the list, this would be fine. But since you're adding it to the start, it will cause the issue you're seeing.

I'd suggest you make each todo an object, and add a unique identifier to each object. This can be a simple integer that you increment. Then you can use that property as the key.

Example

let id = 1;

Vue.createApp({
  data() {
    return {
      todo: "",
      todo_list: [{
        id: id++,
        value: 'last'
      }],
    };
  },

  methods: {
    add() {
      const todo = {
        id: id++,
        value: this.todo
      }
      this.todo_list.unshift(todo);
    },
    remove(index) {
      this.todo_list.splice(index, 1);
    },
  },
}).mount('#app');
.list-enter-active,
.list-leave-active {
  transition: all 200ms ease-out;
}

.list-enter-from {
  transform: translateY(-20px);
}

.list-leave-to {
  opacity: 0;
  transform: translateX(20px);
}

article {
  width: 50%;
  padding: 10px;
  background-color: #dddddd;
  border: 1px solid #cfcfcf;
  border-radius: 5px;
  margin-block: 10px
}
<script src="https://unpkg.com/vue@next"></script>

<div id="app">
  <input type="text" v-model="todo" /> <button @click="add">add</button>
  <transition-group name="list" tag="section">
    <article v-for="(todo, index) in todo_list" :key="todo.id" @click="remove(index)">
      <span>{{ todo.value }}</span>
    </article>
  </transition-group>
</div>

Upvotes: 3

Related Questions