Animating each individual element within the v-for loop in Vue.JS

Recently, I developed a basic to-do app using VueJS. Additionally, I integrated vue2-animate, which is a Vue.js 2.0 adaptation of Animate.css used for Vue's built-in transitions. The animation feature that adds an element functions correctly.

However, there are two issues that need addressing without unnecessary coding:

  1. The animation display for the list fetched from local storage currently runs simultaneously for all items. I require the animation to run sequentially for each item individually.
  2. There is a problem with the deletion animation - it always removes the last item first, followed by a shift in the sequence.

P.S.: To see the demo, refer to JSFiddle since local storage does not function in SO snippets.

Vue.component("adder", {
  data: function() {
    return {
      task: ""
    };
  },
  template: `
    <div class="input-group mb-3">
    <input type="text" class="form-control" placeholder="New task..." aria-label="New task..." aria-describedby="" v-model="task" v-on:keyup.enter="add">
    <div class="input-group-append">
      <button class="btn btn-primary" id="" v-on:click="add" >+</button>
    </div>
    </div>
    `,
  methods: {
    add: function() {
      this.$emit("addtodo", {
        title: this.task,
        done: false
      });
      this.task = "";
    }
  }
});

Vue.component("todo", {
  props: ["item"],
  template: `

    <a href="#" class="list-group-item list-group-item-action task" v-bind:class="{'disabled done' : item.done==true}">
    <label class="form-check-label">
            <input class="form-check-input" type="checkbox" name="" id="" value="checkedValue"  v-model="item.done"> {{item.title}}
        </label>
        <button type="button" class="close" aria-label="Close" v-on:click="del">
            <span aria-hidden="true">&times;</span>
        </button>
    </a>
    
    `,
  methods: {
    del: function() {
      this.$emit("deletetodo");
    }
  }
});

Vue.component("todos", {
  props: ["items"],
  template: `
    <div class="list-group">
        <transition-group name="bounceLeft" tag="a">
            <todo v-for="(item, index) in items" :key="index" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>
        </transition-group>
    </div>
    `,
  methods: {
    delTodo: function(i) {
      this.$emit("deletetodo", i);
    }
  }
});
Vue.config.devtools = true;

let app = new Vue({
  el: ".todoapp",
  data: {
    title: "To-Do App",
    items: []
  },
  methods: {
    addTodo: function(e) {
      this.items.push(e);
    },
    delTodo: function(i) {
      this.items = this.items.filter(e => e != i);
    }
  },
  mounted() {
    if (localStorage.items) {
      this.items = JSON.parse(localStorage.getItem("items"));
    }
  },
  watch: {
    items: {
      handler(val) {
        localStorage.setItem("items", JSON.stringify(this.items));
      },
      deep: true
    }
  }
});
.done>label {
  text-decoration: line-through;
}

.task {
  padding-left: 36px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <title>To-Do App</title>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />

  <link rel="stylesheet" href="https://unpkg.com/vue2-animate/dist/vue2-animate.min.css" />
  <link rel="stylesheet" href="style.css" />
</head>

<body>
  <div class="container todoapp">
    <div class="row">
      <br />
    </div>
    <div class="card">
      <div class="card-header">
        {{ title }}
      </div>
      <div class="card-body">
        <adder v-on:addtodo="addTodo"></adder>
        <todos :items.sync="items" v-on:deletetodo="delTodo"></todos>
      </div>
    </div>
  </div>
  <script src="https://cdn.jsdelivr.net/npm/vue"></script>
  <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
  <script src="script.js"></script>
</body>

</html>

Check out the JSFiddle demo here

Answer №1

Let's tackle this step by step:

Removing a task

The reason why it always appears that the last task is being deleted is because you are using the index as the key for your list items. When you replace the entire items array in your delTodo method, a new array with new keys for each item is generated. If you key by item, then you will get the intended result:

<todo v-for="(item, index) in items" :key="item" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>

Displaying tasks one at a time upon loading

I suggest handling the showing/hiding of tasks with a computed property:

computed: {
    tasks: function(){
        return this.items.filter(item => item.isVisible);
    }
}

This approach involves toggling the value of isVisible on each task for show/hide functionality.

For example, when initially loading tasks from local storage, you could set all tasks to isVisible: false. Then, utilize a setTimeout within a for loop to display them one by one:

mounted() {
    // Retrieve items and initialize all as hidden
    if (localStorage.items) {
        this.items = JSON.parse(localStorage.getItem("items"))
                     .map(item => item.isVisible = false);
    }

    // Iterate through to reveal the tasks
    for(let i=1; i<=this.items.length; i++){
        // Define 300 milliseconds delay
        let delay = i * 300;

        setTimeout(function(){
            this.items[i].isVisible = true;
        }.bind(this), delay);
    }
},

Answer №2

One great success we experienced was the phased addition of items to the array:

  mounted() {
    let items = [];
    if (localStorage.items) {
      items = JSON.parse(localStorage.getItem("items"))
    }
    for (let i = 0; i < items.length; i++) {
      let delay = i * 1000;
      setTimeout(
        function() {
          this.items.push(items[i])
        }.bind(this),
        delay
      )
    }
  }

Answer №3

Adding onto the discussion, here is a method to achieve staggering within a Vuex Action and utilizing fat arrow syntax:

async fetchRepositories( {commit} ){
  const response = await gitHubApi.get<Repository[]>('/users/rodolphocastro/repos') // Using Axios to call API
  const staggered: Repository[] = []
  response.data.forEach((r, i) => {
    const delay = i * 300 // 300ms -> Time interval for each item in the array
    setTimeout(() => {
      staggered.push(r)
      commit('setRepositories', staggered)
    }, delay)
  })
}

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

What is the best way to showcase the information of each object on a click event in Vue.js?

My goal with this code is to display each day's class schedule when it is clicked on. However, the issue I'm facing is that the entire week's schedule is being displayed instead of just the selected day. What adjustments should I make in ord ...

Issue with Kendo dropdown's optionLabel functionality malfunctioning

Check out the Kendo code snippet below for a dropdown control. I'm currently facing an issue where I am trying to display a "Please select" option in the dropdown. This code works perfectly fine for all other dropdowns except for this specific one. T ...

Could using 'require' in node.js lead to a memory leak issue?

I have been working on a program that experiences continuous heap growth. The program is quite simple - it repeatedly tries to load an external file (SyntaxError) using require. However, this external module fails to load due to a syntax error present in i ...

Display user information from another component using Vue's dynamic routing feature

In my UserList.vue component, I have a list of users that I want to display on individual user profiles in the SingleUser.vue component. What is the easiest way to achieve this? The user details are stored in the UserList.vue component. When a specific us ...

Errors encountered by the Chrome extension

Hey there, I've recently delved into creating a chrome extension but hit a roadblock. My issue revolves around the service worker registration failing and encountering errors related to undefined properties. https://i.stack.imgur.com/bGzB4.png The c ...

Methods to troubleshoot problem of angular component loading issue in firefox with ViewEncapsulation.Native

I am encountering a problem with loading an angular component using ViewEncapsulation.Native in firefox, edge, and ipad chrome. Interestingly, there is no issue on safari on mac, chrome on windows, or chrome on android. Error: hostEl.createShadowRoot is ...

What is causing the backslash character to be removed from my ajax request?

When using Ajax to call a rest endpoint, the request takes two parameters: user and permission. $.ajax({ type: 'GET', cache: false, url: "/app/Rest/4.0/UserManagement/AddPermissionToUser", data: { username: encodeURI(user ...

Managing Server Crashes in Node.js

Is there a way to automatically update the database whenever my node.js server crashes or stops? Similar to how try{}catch(){}finally(){} works in JAVA. I am new to this. Does Node emit any events before shutting down so that I can run my function then? ...

Displaying success and failure messages on a modal window using Ajax in PHP form

Unable to display error messages or success messages in the same modal window. I have set up Wordpress and using the following code: Form <form id="formcotizador" method="post" action="<?php echo get_template_directory_uri(); ?>/cotizador/procc ...

The process of building a Vue application encountered an error when attempting to run the command "npm run build"

I recently cloned a Vue application from GitHub (https://github.com/jimmerioles/progressive-weather-app) for setting up automated deployment in Jenkins. Before proceeding, I'm testing it on my Ubuntu machine (GCP VM). I have already installed Java, No ...

Encountered an error when attempting to use 'appendChild' on 'Node': the first parameter is not the correct type. It was able to work with some elements, but not with others

I'm currently working on a script that utilizes fetch to retrieve an external HTML file. The goal is to perform some operations to create two HTMLCollections and then iterate over them to display a div containing one element from each collection. Here ...

Nodemailer fails to send out emails despite the absence of any error messages

I'm currently working on setting up a confirmation email feature for user sign-ups on my website. I've tackled similar tasks in the past, but this time I've hit a roadblock - the emails are not being sent, and there are no error messages to ...

Organizing DIVs upon website initialization

My website features a layout with three columns: <div id="column1"></div> <div id="column2"></div> <div id="column3"></div> I currently have 3 divs on the webpage: <div id="1">aaa</div> <div id="2">b ...

Is the branch of ExtJS 4.1 TreeStore lazy loading extending?

I am working on implementing lazy loading of tree branches in an MVC application using extjs4.1. The branches are located on different URLs and I have faced several challenges along the way. Unfortunately, at this point, the branching functionality is not ...

A large canvas displaying a single optimized image

Hello, I have a large image on the canvas that measures around 10,000 pixels by 10,000 pixels. I am looking for zoom in/out functionality. Can you recommend what technology I should use? Should I consider splitting the image into smaller segments like Go ...

I am experiencing an issue where the jquery sleep function is not being executed during

I currently have an Ajax request that is awaiting a response from another process. function checkProcess() { var flag = 0; while (flag === 0) { $.ajax({ url: "cs/CheckForProcess", async: false, success: ...

Validation of input using JQuery

I am currently working on validating a form, with a specific field that needs to be required. Here is the code for the field in question: <p> <label for="lf">Name: </label> <input class="lf" name="name" type="text"/> < ...

Tips for removing a row without impacting the rest of the rows

I'm currently developing a VueJs parent component with the ability to generate rows dynamically. This component invokes another component responsible for populating two dropdowns using axios - one for categories and the other for subcategories (with t ...

Enhanced hierarchical organization of trees

I came across this code snippet: class Category { constructor( readonly _title: string, ) { } get title() { return this._title } } const categories = { get pets() { const pets = new Category('Pets') return { ge ...

Changing the color of a div while implementing a show and hide effect in JavaScript

I have designed a checkout system with three distinct parts - shipping information, billing information, and order confirmation. These sections are all on the same page, allowing for a seamless flow during the checkout process. Once a customer completes t ...