Vue 練習 - Todo list 待辦事項清單

Demo

Vue 練習 - Todo List 待辦事項清單 - Demo

Introduction 介紹

以 Vue 實作代辦事項清單的新增、刪除、修改。

  • 可動態切換 ClassName 與 style
  • 運用指令與修飾符
  • 表單資料綁定
  • 動態新增、刪除、修改

建立待辦事項的列表

有一個 input 欄位,現在要在輸入後把文字加入代辦事項列表,首先設定一個代辦事項列表的基礎結構,包括:

鍵入新增項目的輸入框、

  • 點擊後把輸入項目加入 vue 原始碼的按鈕 (或鍵盤動作)
  • 以陣列儲存資料內容的區段

首先處理新增項目的輸入框,透過使用v-model可以實現 HTML 與數據結構的雙向綁定,我們可以把輸入項目新增儲存到資料結構裡面。假設我在表單組件 input 上設置 v-model 並綁定到 data 中的 meesage 上,他除了會即時顯示 data.message 中資料以外,我們在更動 input 中的數值時也會同步更改回 data 中 message 的 value。

每個輸入的項目都具備一個獨一的 id,透過 v-bind (縮寫:id),更新 HTML 屬性,便可以動態的去綁定、控制屬性,進而使得該元素變成是可控制的元素。 (因為隨後我們要新增、刪除或修改這些項目)。但是使用者互動輸入的項目需要觸發一些事件才能完成,所以這裡使用v-on與修飾符,綁定添加項目的方法。v-on 顧名是類似於原生 JavaScript 中的 on 事件,在後面寫上要觸發行為的條件(click),並且在 value 的值內放入指定的 method。

動態產生資料

接著處理資料的畫面顯示,資料是由使用者輸入後,儲存到陣列裡面,再動態產生在。所以首先處理已綁定在輸入按鈕與欄位的 addTodo 方法。此方法把輸入的資訊、依照 timestamp而有的 id 、是否進行運算等性質,加入todos 陣列裡面。並清空輸入欄位。

再以 v-for 動態產生資料畫面上,可以加上兩個參數,以一個名稱做代表,雷同於一般物件的值,第二個參數是該筆資料的實際索引值。最後使用雙花括號與資料連結。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<input
type="text"
class="form-control"
placeholder="準備要做的任務"
v-model="newTodo"
:id="item.id"
@keyup.enter="addTodo"
/>
<button class="btn btn-primary" @click="addTodo">新增</button>

<li class="todoItem" v-for="item in todos">
<label class="form-check-label" :for="item.id">
{{item.title}}
<button type="button" class="btn-delete">刪除</button>
<br />
</label>
<input type="text" class="form-control" />
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
var app = new Vue({
el:'#app',
data:{
//儲存新增的待辦事項
newTodo:''
//一個陣列,儲存目前待辦事項的內容
todos:[{
//對應checkbox與待辦事項
id:'345',
title: '你好',
completed:false
}]

},
methods:{
//新增一個待辦事項的方法
addTodo:function(){
var value = this.newTodo;
var timestamp = Math.floor(Date.now());
if(!value){
return
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = ''
}
}
});

刪除陣列上的特定資料

新增資料後,現在要將它刪除,在刪除按鈕上新增@click="removeTodo()"方法,為了知道刪除的是哪一筆資料,所以必須在畫面資料列表(item,key)in todoskey是陣列索引位置,同時將參數傳入刪除按鍵與刪除方法。在 methods 裡新增一個判斷式,如果輸入值是空的就不能新增;並以 trim()刪除多餘空白鍵。最後以splice() 方法刪除所存在位置(key) 的資料。

1
2
3
4
5
6
7
8
9
10
11
12

<input type="text" class="form-control" placeholder="準備要做的任務" v-model="newTodo " :id="item.id" @keyup.enter="addTodo">
<button class="btn btn-primary" @click="addTodo">新增</button>

<li class="todoItem" v-for="(item,key)in todos">
<label class="form-check-label" :for="item.id">
{{item.title}}
<button type="button" class="btn-delete"click="removeTodo(key)">刪除</button>
<br>
</label>
<input type="text" class="form-control">
</li
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var app = new Vue({
el:'#app',
data:{
newTodo:''
todos:[{
id:'345',
title: '你好',
completed:false
}]

},
methods:{
addTodo:function(){

var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());

if(!value){
return
}
this.todos.push({
id: timestamp,
itle: value,
computed: false
});
this.newTodo = ''
},
removeTodo:function(key){
this.todos.splice(key,1)
}
}
});

製作頁籤分類功能

代辦事項頁面需有 3 個頁籤:全部項目(包含進行與完成)、進行中、已完成。所以我們現在動態呈現在畫面的資料,都是過濾過的。因此 v-for 要綁定
(item,key)in filteredTodos。使用這個過濾方法需使用 computed運算,裡面的判斷式的邏輯是:如果:點選全部項目頁籤,直接回傳全部內容;如果點選進行中頁籤,則表示這些項目不是已完成的;如果點選已完成,則表示這些項目已完成。要在資料結構裡面,新增變數 visibility 決定切換呈現的頁籤與其內容:利用動態切換 className 功能製造刪節線效果::class="{'completed':item.completed}";利用動態切換 className 功能動態切換頁籤,如果 className 是 all 的時候,visibility 就切換為 all。切換條件是「按下頁籤」:class="{'active':visibility == 'all'}" @click="visibility='all'"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<input
type="text"
class="form-control"
placeholder="準備要做的任務"
v-model="newTodo "
:id="item.id"
@keyup.enter="addTodo"
/>
<button class="btn btn-primary" @click="addTodo">新增</button>

<li class="todoItem" v-for="(item,key)in filteredTodos">
<input
type="checkbox"
class="form-check-input"
v-model="item.completed"
:id="item.id"
/>

<label
class="form-check-label"
:class="{'completed':item.completed}"
:for="item.id"
>
{{item.title}}

<button type="button" class="btn-delete"click="removeTodo(key)">
刪除
</button>
<br />
</label>

<input type="text" class="form-control" />
</li>

<div class="control">
<a
href="#"
class="btn"
:class="{'active':visibility == 'all'}"
@click="visibility='all'"
>全部</a
>
<a
href="#"
class="btn"
:class="{'active':visibility == 'active'}"
@click="visibility='active'"
>進行中</a
>
<a
href="#"
class="btn"
:class="{'active':visibility == 'completed'}"
@click="visibility='completed'"
>已完成</a
>
<a class="btn" href="#">清除所有任務</a>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var app = new Vue({
el:'#app',
data:{
newTodo:''
todos:[{
id:'345',
title: '你好',
completed:false
}],
//新增變數
visibility:'all'

},
methods:{

addTodo:function(){

var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());

if(!value){
return
}
this.todos.push({
id: timestamp,
itle: value,
computed: false
});
this.newTodo = ''
},

removeTodo:function(key){
this.todos.splice(key,1)
}
},

//過濾
computed: {
filteredTodos: function () {
if (this.visibility == 'all') {
return this.todos;
} else if (this.visibility == 'active') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'completed') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
return [];
}
}

});
1
2
3
4
5
<style>
.completed {
text-decoration: line-through
}
</style>

雙擊修改資料內容

在顯示內容列新增一個雙擊觸發編輯內容方法@dblclick="editTodo(item)",這個點擊的項目會暫時除存在暫存編輯內容cacheTodo:{}cacheTitle裡面。這時要從編輯框顯示要修改的內容,所以畫面以判斷式替換成編輯輸入框。反之,假如原本畫面上的資料的 id 不等於暫存編輯中的 id,就顯示出來

v-if="item.id ===cacheTodo.id",並且連結資料結構裡的暫存邊集中資料內容 v-model="cacheTitle"。同一個編輯輸入框,還有儲存編輯內容與取消兩個輸入行為。

已完成的編輯內容方法 @keyup.enter="doneEdit(item),把剛剛修改的內容儲存到被修改的項目、並將該暫存物件清空。取消編輯則是讓暫存物件清空。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<li
class="todoItem"
v-for="(item,key) in filteredTodos"
@dblclick="editTodo(item)"
>
//判斷是否顯示編輯框
<div class="d-flex" v-if="item.id !==cacheTodo.id">
<input
type="checkbox"
class="form-check-input"
v-model="item.completed"
:id="item.id"
/>
<label
class="form-check-label"
:class="{'completed':item.completed}"
:for="item.id"
>
{{item.title}}
<button type="button" class="btn-delete" @click="removeTodo(key)">
刪除
</button>
<br />
</label>
</div>
<input
type="text"
class="form-control"
v-if="item.id ===cacheTodo.id"
v-model="cacheTitle"
@keyup.esc="cancelEdit()"
@keyup.enter="doneEdit(item)"
/>
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var app = new Vue({
el:'#app',
data:{
newTodo:''
todos:[{
id:'345',
title: '你好',
completed:false
}],

cacheTodo: {},
cacheTitle: '',
},
methods:{
editTodo: function (item) {
this.cacheTodo = item;
this.cacheTitle = item.title;
},
//取消編輯
cancelEdit: function () {
this.cacheTodo = {}
},
//儲存完成的編輯
doneEdit: function (item) {
item.title = this.cacheTitle;
this.cacheTitle = '';
this.cacheTodo = {}
}
});

修改刪除項目的索引

修正刪除項目的索引位置,刪除項目鍵本來傳入的是資料在陣列中的索引位置,但是因為現在它可能位於不同的標籤,也就是可能勾選完成,所以在每個頁籤的索引位置都不一樣,因此函數參數要改成 todo 本身,讓它的新索引等於目前它在該標籤裡的 key值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<li
class="todoItem"
v-for="(item,key) in filteredTodos"
@dblclick="editTodo(item)"
>
<div class="d-flex" v-if="item.id !==cacheTodo.id">
<input
type="checkbox"
class="form-check-input"
v-model="item.completed"
:id="item.id"
/>
<label
class="form-check-label"
:class="{'completed':item.completed}"
:for="item.id"
>
{{item.title}}
<button type="button" class="btn-delete" @click="removeTodo(item)">
刪除
</button>
<br />
</label>
</div>

<input
type="text"
class="form-control"
v-if="item.id ===cacheTodo.id"
v-model="cacheTitle"
@keyup.esc="cancelEdit()"
@keyup.enter="doneEdit(item)"
/>
</li>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
var app = new Vue({
el:'#app',
data:{
newTodo:''
todos:[{
id:'345',
title: '你好',
completed:false
}],
//暫存編輯事項
cacheTodo: {},
cacheTitle: '',

visibility:'all'

},
methods:{

addTodo:function(){

var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());

if(!value){
return
}
this.todos.push({
id: timestamp,
itle: value,
computed: false
});
this.newTodo = ''
},

removeTodo: function (todo) {
var newIndex = "";
var vm = this;
vm.todos.forEach(function (item, key) {
if (todo.id === item.id) {
newIndex = key
}

})
this.todos.splice(newIndex, 1)
},,


});

刪除全部及還有幾筆任務未完成

刪除全部的方法就是讓儲存資料的陣列清空;還沒完成的項目,以判斷式判斷,如果不是完成的,就把還沒完成的內容數量,傳到畫面上。

1
2
3
4
5
6
7
<div class="control">
<a class="btn" href="#" @click="clearAll">清除所有任務</a>
</div>

<div class="footer">
<span>還有 {{undoneRecords }} 筆任務未完成</span>
</div>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
methods:{

clearAll: function () { this.todos = [];
}},

computed: {
undoneRecords:function(){
var undoneRecords=[];
this.todos.forEach(function(item){
if(!item.completed){
undoneRecords.push(item);
}
})
return.undoneRecords.length;
}}

Powered by Hexo and Hexo-theme-hiker

Copyright © 2013 - 2020 CYC'S BLOG All Rights Reserved.

UV : | PV :