鐵人賽終於來到最後兩篇(最後一天是廢文心得文)
這兩天來個大顆ㄧ點(跟前幾篇比)的栗子,用 Vue.js 實作一個「一眼瞬間」版的 ToDoList。
為什麼叫「一眼瞬間」呢?因為 F5 刷新就沒啦! 先來做這個最基本連 localStorage 都沒有的 ToDoList,當做 Vue.js 應用練習。
但怕被認為這樣太混,再多加一項初始載入指定的 API(Axios GET)。
練習素材:
先來將該做的事情列成清單:
(data)資料格式定義,每筆待辦項有:'id' / 'title' / 'completed'
可以讀取(R)初始資料(API GET)
可以新增(C)待辦項並即時顯示(預設狀態為「未完成」)
可以編輯(U)目前的待辦項
可以刪除(D)待辦項
可以一次刪除所有待辦項
頁籤(Tab)依完成狀態分類清單:全部 / 進行中 / 已完成
計數目前未完成的項,且有變動時即時更新計數
(下列 HTML 的部分這邊只截取有應用到 Vue.js 的部分,也就是要觸發事件 / 進行資料綁定的,完整版請看 Codepen)
<div class="container" id="app">
<input class="form-control" type="text" placeholder="準備要做的任務"
v-model="newTodo" @keyup.enter="addTodo"/>
<div class="input-group-append">
<button class="btn" type="button" @click="addTodo">新增</button>
<template v-for="(item, index) in visibilityList">
<li class="nav-item" :key="index"></li>
<a class="nav-link" href="#" :class="{ active: visibility == item.value }"
@click="(visibility = item.value)">{{ item.name }}
</a>
</template>
<template v-for="(item) in filteredTodos">
<li class="list-group-item" :key="item.id" @dblclick="editTodo(item)">
<div v-if="item.id !== cacheTodo.id">
<input class="form-check-input" :id="item.id" type="checkbox"
@click="changeComplated(item.id)"
v-model="item.completed"/>
<label class="form-check-label" :class="{ completed: item.completed }"
:for="item.id"> {{ item.title }}
</label>
<button class="close" type="button" aria-label="Close"
@click="removeTodo(item)">×
</button>
</div>
<input class="form-control" type="text"
v-model="cacheTitle" v-if="item.id == cacheTodo.id"
@keyup.esc="cancelEdit" @keyup.enter="doneEdit(item)"/>
</li>
</template>
</div>
<div class="card-footer">
{{ `還有${activeTodosLength}筆任務未完成` }}
<a href="#" @click="cleanTodo">清除所有任務</a>
</div>
</div>
var urlAPI = "https://eudora-hsj.github.io/Vue-practice/data/todolist.json"
var app = new Vue({
el: "#app",
data() {
return {
newTodo:'',
todos: [],
visibilityList: [
{ name: "全部", value: "all" },
{ name: "進行中", value: "active" },
{ name: "已完成", value: "completed" }
],
visibility: 'all',
cacheTodo: {},
cacheTitle: '',
}
},
created() {
this.getList(urlAPI)
},
methods: {
getList(url) {
axios
.get(url)
.then((res) => {
this.todos = res.data.data
})
.catch((err) => {
console.log(err)
})
},
addTodo() {
let newTodoStr = this.newTodo.trim()
if (!newTodoStr) {
return
}
this.newTodo = ""
let submitData = {
id: Math.floor(Date.now()),
title: newTodoStr,
completed: false
}
this.todos.push(submitData)
},
removeTodo(item) {
this.todos.splice(this.getIndex(item.id), 1)
},
editTodo(item) {
this.cacheTodo = item
this.cacheTitle = item.title
},
cancelEdit() {
this.cacheTodo = {}
},
doneEdit(item) {
item.title = this.cacheTitle
this.cacheTitle = ""
this.cacheTodo = {}
},
cleanTodo() {
this.todos.splice(0, this.todos.length)
},
getIndex(id) {
return this.todos.findIndex((el) => el.id == id)
},
changeComplated(id){
let index = this.getIndex(id)
this.todos[index].completed = !this.todos[index].completed
}
},
computed: {
filteredTodos() {
let nowTab = this.visibility
switch (nowTab) {
case "all":
return this.todos.filter((item) => true)
case "active":
return this.todos.filter((item) => !item.completed)
case "completed":
return this.todos.filter((item) => item.completed)
}
},
getNewKey() {
return Math.max(...this.todos.map((el) => +el.id))
},
activeTodosLength() {
return this.todos.filter((item) => !item.completed).length
}
}
})
~ 待下篇繼續囉!
個人 Blog: https://eudora.cc/