I'm trying to make a client-side-only todolist that uses VueJS (2.x). Here 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/[email protected]/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/[email protected]/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 issue is simple: when I change the state of a checkbox (checking it or unchecking it) in a given list, the checkbox that directly follows it switches state as well.
I can't figure out why this happens and how to fix it. If one can tell me why this happens (so that I don't have to come back here whenever I have a misbehaving v-for
/switch-state) as well as how to fix it, that would be great!
CodePudding user response:
- first of all. You'd better use
Function
indata
instead ofObject
, or it may cause an update error.
Since Function
is accepted only when used in a component definition.
// wrong
data: {
tasks: []
}
// correct
data() {
return {
task: []
}
}
- You may not change the
computed
attribute directly which only has aComputed Getter
in default. If you want to handle thecomputed
attribute, give aComputed 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;
}
});
}
}
}
- you can't bind the value via
v-for
, because vue2 can't recognize the changes in the array.
<!-- wrong -->
<li
v-for="item in complete">
{{ item.description }}
<input
type="checkbox"
:checked="item.done"
@change="item.done = false">
</li>
<!-- correct -->
<li
v-for="(item, index) in complete">
{{ item.description }}
<input
type="checkbox"
:checked="item.done"
@change="complete[index].done = false">
</li>
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;
}
});
}
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<h1>To-do List</h1>
<h2>Completed Tasks!</h2>
<ul>
<li
v-for="(item, index) in complete">
{{ item.description }}
<input
type="checkbox"
v-model="complete[index].done">
</li>
</ul>
<h2>Uncompleted Tasks!</h2>
<ul>
<li
v-for="(item, index) in uncomplete">
{{ item.description }}
<input
type="checkbox"
v-model="uncomplete[index].done">
</li>
</ul>
</div>