I have a todolist that you can add new todo, and mark each todo as completed by click checkbox, and select the item visibility of the list by all or active or completed on the footer.
When I select Completed on the footer, it only shows item that is completed.
The problem is when I unmark the completed item in completed view, the remain(especially next one) item's checkbox display incorrectly.
var STORAGE_KEY = 'todo mvc by vue';
var todoStorage = {
data: [],
fetch: function () {
return this.data;
},
save: function (data) {
this.data = data;
//localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
}
}
var filters = {
all: function (todos) {
return todos;
},
active: function (todos) {
return todos.filter(todo => !todo.completed);
},
completed: function (todos) {
return todos.filter(todo => todo.completed);
}
}
var app = new Vue({
el: '.app',
data: {
todos: todoStorage.fetch(),
newTodo: '',
visiblitys: [{
title: 'All',
selected: true,
filter: filters.all
}, {
title: 'Active',
selected: false,
filter: filters.active
}, {
title: 'Completed',
selected: false,
filter: filters.completed
}]
},
computed: {
todosForShow: function () {
return this.currentFilter(this.todos);
},
currentFilter: function () {
return this.visiblitys.find(v => v.selected).filter;
}
},
watch: {
todos: {
handler: function (todos) {
todoStorage.save(todos);
},
deep: true
}
},
methods: {
deleteItem: function (todo) {
this.todos.splice(this.todos.indexOf(todo), 1);
},
deleteCompletedItem: function () {
this.todos = filters.active(this.todos);
},
addItem: function (todo) {
var value = this.newTodo && this.newTodo.trim();
if (!value) return;
this.todos.push({
content: value,
completed: false
});
this.newTodo = '';
},
selectVisiblity: function (visiblity) {
this.visiblitys.forEach(vis => vis.selected = false);
visiblity.selected = true;
},
completeAll: function () {
this.todos.forEach(todo => todo.completed = true);
}
}
});
* {
box-sizing: border-box;
}
header h1 {
text-align: center;
}
.container {
max-width: 800px;
margin: 0 auto;
}
.new-todo {
padding: 16px;
box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
font-size: 24px;
width: 100%;
}
li.completed {
text-decoration: line-through;
}
footer {
border-top: 1px solid #e6e6e6;
height: 20px;
padding: 10px 15px;
color: #777;
text-align: center;
position: relative;
}
.filters {
list-style: none;
margin: 0;
padding: 0;
position: absolute;
left: 0;
right: 0;
}
.filters li {
display: inline;
}
.filters li a {
margin: 3px;
padding: 3px 7px;
text-decoration: none;
border: 1px solid transparent;
border-radius: 3px;
color: inherit;
}
.filters li a.selected {
border-color: rgba(175, 47, 47, 0.2);
}
.todo-count {
float: left;
text-align: left;
}
.complete-all {
float: right;
cursor: pointer;
position: relative;
color: black;
}
.todo-list {
clear: both;
}
.clear-completed {
float: right;
cursor: pointer;
position: relative;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
font-weight: inherit;
color: inherit;
-webkit-font-smoothing: antialiased;
}
<header>
<h1>Todos</h1>
</header>
<div class="app container">
<input class="new-todo" type="text" v-on:keyup.enter="addItem()" v-model="newTodo"
placeholder="What needs to be done?">
<section class="main" v-show="todos.length > 0">
<button class="complete-all" @click="completeAll()">Complete All</button>
<ul class="todo-list">
<li v-for="todo in todosForShow" :class="{completed: todo.completed}">
<input type="checkbox" v-model="todo.completed">
{{todo.content}} -
<a href="#" @click.prevent="deleteItem(todo)">remove</a>
</li>
</ul>
<footer>
<span class="todo-count">{{todos.filter(t=>!t.completed).length}} items left</span>
<ul class="filters">
<li v-for="visiblity in visiblitys">
<a href="#" :class="{selected: visiblity.selected}"
@click.prevent="selectVisiblity(visiblity)">{{visiblity.title}}</a>
</li>
</ul>
<button class="clear-completed" v-show="todos.some(t=>t.completed)" @click="deleteCompletedItem()">Clear
Completed</button>
</footer>
</section>
<br>todos raw data:{{this.todos}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>