Home  >  Q&A  >  body text

How can I make a list containing checkboxes attached to an object in a state that a v-for directive toggles (without toggling other checkboxes in the list)?

I'm trying to make a client-side to-do list using VueJS (2.x). This is my HTML:

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>To-do List</title>
        <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
    </head>
    <body>
        
        <div id="app">
            <h1>To-do List</h1>
            <h2>Completed Tasks!</h2>
            <ul>
                <li v-for="item in complete">{{ item.description }}<input type="checkbox" :checked="item.done" @change="item.done = false"></li>
            </ul>
            <h2>Uncompleted Tasks!</h2>
            <ul>
                <li v-for="item in uncomplete">{{ item.description }}<input type="checkbox" :checked="item.done" @change="item.done = true"></li>
            </ul>
        </div>
        
        <script src="https://cdn.jsdelivr.net/npm/vue@2.7.13/dist/vue.js"></script>
        <script type="module" src="scripts/app.js"></script>
    </body>
    </html>

Then in scripts/app.js I did this:

    'use strict'
    
    let app = new Vue({
    
        el      : "#app",
        data    : {
            tasks: [
                { description: 'Say Hello', done: true },
                { description: 'Review Code', done: false },
                { description: 'Read Emails', done: false },
                { description: 'Reply to Emails', done: false },
                { description: 'Wash The Dishes', done: true },
                { description: 'Stop Working', done: true },
            ]
        },
        computed : {
            complete : function() {
                return this.tasks.filter(task => task.done === true);
            },
            uncomplete : function() {
                return this.tasks.filter(task => task.done === false);
            }
        }
    
    });

My problem is simple: when I change the state of a checkbox in a given list (check or uncheck it), the checkbox immediately following it also switches state.

I don't understand why this happens and how to fix it. If anyone could tell me why this is happening (so that I don't have to come back here when v-for/switch-state misbehaves) and how to fix it, that would be great!

P粉738346380P粉738346380203 days ago343

reply all(1)I'll reply

  • P粉312195700

    P粉3121957002024-03-30 21:46:29

    1. first. It is best to use Function instead of Object in data, otherwise it may cause update errors.

    Because Function is only accepted when used in a component definition.

    // wrong
    data: {
        tasks: []
    }
    
    // correct
    data() {
        return {
            task: []
        }
    }
    
    1. You cannot directly change the compulated property, which has only one Compulated Getter by default. If you want to handle a compated property, give a computed setter to it.
    // wrong
    computed: {
        complete(){
          return this.tasks.filter(task => task.done === true);
        },
    }
    
    // right
    computed: {
        complete: {
            get() {
                return this.tasks.filter(task => task.done === true);
            },
            set(value) {
                this.task.forEach((task, index) => {
                    let matched = value.find(({ description }) => description === task.description);
                    if(matched) {
                        this.task[index] = matched;
                    }
                });
            }
        }
    }
    
    1. Cannot bind values ​​via v-for because vue2 does not recognize changes in the array.
    
    
  • {{ item.description }}
  • {{ item.description }}

  • new Vue({
      el: "#app",
      data() {
        return {
          tasks: [
            { description: 'Say Hello', done: true },
            { description: 'Review Code', done: false },
            { description: 'Read Emails', done: false },
            { description: 'Reply to Emails', done: false },
            { description: 'Wash The Dishes', done: true },
            { description: 'Stop Working', done: true },
          ]
        };
      },
      computed : {
        complete: {
          get() {
            return this.tasks.filter(task => task.done === true);
          },
          set(value) {
            this.task.forEach((task, index) => {
              let matched = value.find(({ description }) => description === task.description);
              if(matched) {
                  this.task[index] = matched;
              }
            });
          }
        },
        uncomplete: {
          get() {
            return this.tasks.filter(task => task.done === false);
          },
          set(value) {
            this.task.forEach((task, index) => {
              let matched = value.find(({ description }) => description === task.description);
              if(matched) {
                  this.task[index] = matched;
              }
            });
          }
        }
      }
    });
    sssccc
    

    To-do List

    Completed Tasks!

    • {{ item.description }}

    Uncompleted Tasks!

    • {{ item.description }}

    reply
    0
  • Cancelreply