前言
解了一些 Vue.js 基本的概念,那麼就繼續往下做一些簡單的應用吧!
本節內容包含下述子章節:
先來看看成品長怎樣
套用版型及建立代辦事項列表的資料
作業模板:傳送門
1. 新增變數與函式
newTodo:字串,用於新增新的代辦事項內容todos:陣列,紀錄代辦事項,裡面為key/value,包含id、title與completed(完成狀況)addTodo:methods function,當點擊新增按鈕時,會被觸發的函式。
2. 輸入框與變數雙向綁定
將變數 newTodo 使用 v-model 與 input 建立雙向綁定。
<input type="text" class="form-control" placeholder="準備要做的任務" v-model="newTodo">
3. 按鈕點擊事件與函式綁定
將 addTodo 與 button 使用 v-on:click / @click 進行綁定。
<input type="text" class="form-control" placeholder="準備要做的任務" v-model="newTodo">
<div class="input-group-append">
<button class="btn btn-primary" type="button" v-on:click="addTodo">新增</button>
</div>
4. 將 todos 顯示在 UI 上
這邊打算用 label 來顯示文字,且因每筆代辦事項之前,想會加入一個核選方塊,因此使用一個區塊標籤 div,
包覆住 label 與 checkbox,並在 <li> 標籤上使用 v-for='item in todos' 進行綁定。
<li class="list-group-item" v-for="item in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="a1">
<label class="form-check-label" for="a1">
Cras justo odio
</label>
</div>
<button type="button" class="close ml-auto" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
5. 將上述綁定的項目加到 vue 原始碼
在 data 裡面新增 newTodo 及 todos ,還有新增一個 methods 放入要新增代辦事項的 addTodo 的 function 。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [], // 紀錄代辦事項有哪些內容
},
methods: { //新增代辦事項到 list
addTodo: function () {
}
}
});
</script>
6. 在 todos 裡面先試寫一份內容
todos 陣列裡,新增一個物件 { } ,物件裡面需定義
id,這個id是要讓checkbox能夠跟代辦事項內容作對應的。title,代辦事項的內容。completed,代表完成的狀態。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
},
methods: { //新增代辦事項到 list
addTodo: function () {
}
}
});
</script>
7. 將取出的代辦事項內容呈現在 UI 上
將 item.title 使用兩個大括弧 Mustache 語法包覆在 label 標籤中顯示於 UI 上。
<li class="list-group-item" v-for="item in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="a1">
<label class="form-check-label" for="a1">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
8. 將每筆代辦事項的 label 和 checkbox 綁定
- 將所取出的
item的id與checkbox使用v-bind進行綁定,可使用縮寫:id來表示 - 將所取出的
item的id與label使用v-bind進行綁定,可使用縮寫:for來表示
<li class="list-group-item" v-for="item in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" :id="item.id">
<label class="form-check-label" :for="item.id">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
9. 將 checkbox 狀態與 item 的完成狀況進行綁定
將 item.completed 與 checkbox 使用 v-model 建立雙向綁定。
<li class="list-group-item" v-for="item in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="item.completed" :id="item.id">
<label class="form-check-label" :for="item.id">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
10. 實做 addTodo 函式,將 input 新增的內容加入 todos 的列表裡面
- 先宣告一個變數
value儲存newTodo的內容 - 宣告一個變數
timestamp做為item的id,做為title的id使用。這邊使用時間Date.now轉為數字的一種格式,
再透過 Math.floor 轉為正整數。
- 接下來要將
todos內容,使用array.push將新的Object加入todos陣列尾端。 - 最後記得清空輸入欄,
this.newTodo = '';
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo;
var timestamp = Math.floor(Date.now());
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
}
}
});
</script>
11. 支援 enter 新增
除了點擊新增的按鈕外,也可以透過按鍵行為把資料內容新增上去,使用 @keyup.enter="addTodo" ,讓輸入欄可以
支援按 enter 提交。
<input type="text" class="form-control" placeholder="準備要做的任務" v-model="newTodo" @keyup.enter="addTodo">
12. 避免加入空的任務
建議可以針對輸入的內容去刪除前後的空格,在變數 value = this.newTodo; 後面加上 .trim() 。
並判斷是否為空字串,避免加入空字串,加上 if 的判斷式。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
}
}
});
</script>
刪除陣列上的特定資料
1.新增函式並與刪除按鈕綁定
接著我們要在能夠在 todos 代辦事項列表的右方能夠刪除資料,找到程式碼 button 的區塊,
利用 @click="removeTodo" 綁定。
<li class="list-group-item" v-for="item in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="item.completed" :id="item.id">
<label class="form-check-label" :for="item.id">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
綁定按鈕後,在 vue 的 methods 下方建立所需函式 removeTodo
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function () {
}
}
});
</script>
2. 實做 removeTodo 函式
當我們點擊刪除時,還無法得知是按了哪一個項目的刪除,所以在點擊按鈕後必須把相關的參數傳回來。
這邊我們可以調整
<li> 裡面的 v-for ,將 v-for 調整為 v-for="(item, key) in todos" ,這邊的 key 是陣列的索引位置。
<li class="list-group-item" v-for="(item, key) in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="item.completed" :id="item.id">
<label class="form-check-label" :for="item.id">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
在傳送參數的同時會將 key 傳到 button 所綁定的 removeTodo 函式中,所以程式碼須調整為 @click="removeTodo(key)"
<li class="list-group-item" v-for="(item, key) in todos">
<div class="d-flex">
<div class="form-check">
<input type="checkbox" class="form-check-input" v-model="item.completed" :id="item.id">
<label class="form-check-label" :for="item.id">
{{ item.title }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
而函式 removeTodo function 中也包含了 key ,在移除陣列中的值可以使用 array.splice 刪除陣列中某一筆資料,但其指定
方式是使用 index ,也就是我們這邊的變數 key ,而 (key, 1) 中的 1 是代表要刪除幾筆資料的意思。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
}
});
</script>
製作頁籤分類的功能
刪除線效果
在顯示任務名稱的 label 標籤上添加 class 的動態切換,也就是 :class="{'completed': item.completed}" ,物件中的
'completed' 放的是要套用的 class 名稱, item.completed 則是條件。
<li class="list-group-item" v-for="(item, key) in todos">
<div class="d-flex">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
頁籤切換功能
頁籤功能分成兩部份來實做,一是上方頁籤切換,二是下方資料切換。
先實做上方頁簽切換的部份
1. 新增變數
先在 data 裡面新增一個變數 visibility ,此變數用來紀錄目前使用者正在查看的頁簽,並將該值初始化為 allItem。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
visibility: 'allItem'
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
}
});
</script>
新增三個變數 allItem 、 processing 與 done,分別對應到 card-header 的三個分頁:全部、進行中、已完成。
2. 頁簽渲染
為了突顯目前正在查看的頁簽,會將該頁簽進行渲染。在各個頁籤加上 class 的動態切換,當 visibility 指向該頁時,
就加上 active class 的渲染效果。例如 :class="{'active': visibility == 'allItem'}" ,物件中的 'active' 放的是
class 的名稱,冒號 : 後方是條件,當 visibility 指向全部時,所以這邊的 == 後面就要放剛才所宣告的變數 allItem
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link" :class="{'active': visibility == 'allItem'}" href="#">全部</a>
</li>
<li class="nav-item">
<a class="nav-link " :class="{'active': visibility == 'processing'}" href="#">進行中</a>
</li>
<li class="nav-item">
<a class="nav-link" :class="{'active': visibility == 'done'}" href="#">已完成</a>
</li>
</ul>
</div>
3. 頁簽切換效果
為實做為點擊切換的效果,因此當點擊頁籤時需將 visibility 指向自己,例如: @click="visibility = 'allItem'" ,
一旦 visibility 指向自己,:class 的判斷式就會為真,因此就會為該頁簽加上渲染效果。
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a class="nav-link" :class="{'active': visibility == 'allItem'}" @click="visibility = 'allItem'" href="#">全部</a>
</li>
<li class="nav-item">
<a class="nav-link " :class="{'active': visibility == 'processing'}" @click="visibility = 'processing'" href="#">進行中</a>
</li>
<li class="nav-item">
<a class="nav-link" :class="{'active': visibility == 'done'}" @click="visibility = 'done'" href="#">已完成</a>
</li>
</ul>
</div>
下方資料切換
1. 動態過濾陣列內容
因為我們不希望修改原始 todos 中的資料,因此實做時會希望回傳一個新過濾後的陣列。且因為我們希望在不同頁籤會
回傳不同的陣列,因此我們會在 methods 下方新增 computed 並實作名為 filteredTodos 的函式。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
visibility: 'allItem'
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
},
computed:{
filteredTodos: function () {
return this.todos;
}
}
});
</script>
2. 使用回傳陣列取代原始陣列
使用 filteredTodos 取代掉先 v-for 中所使用的 todos。
PS. 如果為確認 filteredTodos 與 v-for 的搭配是否正常運作,可以在 filteredTodos 中直接回傳 todos 做為測試。
<li class="list-group-item" v-for="(item, key) in filteredTodos">
<div class="d-flex">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</li>
3. 實作 filteredTodos 函數
依照 visibility 的內容回不同的過濾結果。實作如下,課程影片中老師是用 forEach 的方式,
不過也可以用 filter 的做法。
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
visibility: 'allItem'
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
}
},
computed:{
filteredTodos: function () {
if (this.visibility == 'allItem') {
return this.todos;
} else if (this.visibility == 'processing') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'done') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
}
}
});
</script>
雙擊修改資料內容
1. 在 UI 上實做輸入框架
準備好一個 input text 元件,並與目前顯示內容的元件 - 也就是剛剛包住 label 與 checkbox 的 div - 放置在同一層。
<ul class="list-group list-group-flush text-left">
<li class="list-group-item" v-for="(item, key) in filteredTodos">
<div class="d-flex">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<input type="text" class="form-control">
</li>
<!-- <li class="list-group-item">
<input type="text" class="form-control">
</li> -->
</ul>
2. 雙擊事件
這邊希望點擊代辦事件兩下後,可以進入修入模式。因此我們在 <li> 標籤建立一個雙擊事件, @dblclick="editTodo(item)"
<ul class="list-group list-group-flush text-left">
<li class="list-group-item" v-for="(item, key) in filteredTodos" @dblclick="editTodo(item)">
<div class="d-flex">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<input type="text" class="form-control">
</li>
<!-- <li class="list-group-item">
<input type="text" class="form-control">
</li> -->
</ul>
3. 實做 editTodo 函數
將剛才新增的雙擊事件 editTodo function 加入 methods ,並將 function 裡的 item 另外存起來,目的是要讓我們知道
哪一筆資料是目前正在編輯中的,所以在資料的部分需要做一些調整,我們在 data 裡面新宣告變數 cacheTodo ( 用於:暫
時存放todo的地方)、 cacheTitle (用於:編輯標題時預存的地方)。
新增完後回到 editTodo function ,將 this.cacheTodo 指向我們傳入的 item ,並且把預存的 this.cacheTitle 指向剛才
我們傳入的 item.title
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
cacheTodo: {},
cacheTitle: '',
visibility: 'allItem',
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
},
editTodo: function (item) {
console.log(item);
this.cacheTodo = item;
this.cacheTitle = item.title;
}
},
computed:{
filteredTodos: function () {
if (this.visibility == 'allItem') {
return this.todos;
} else if (this.visibility == 'processing') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'done') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
}
}
});
</script>
4. 顯示編輯框
畫面中我們原本的 todos 項目與 input 修改框,兩者只會出現其中一個,所以我們要在剛剛包住 label 與 checkbox 的
div 裡加入判斷式, v-if="item.id !== cacheTodo.id" ,此段意指:假設 item.id 不等於 cacheTodo.id 就讓它顯示出
來。
相反的在 input 加入判斷式 v-if="item.id === cacheTodo.id" ,假設 input 的 item.id 等於 cacheTodo.id 就會顯示
input ,並且把原本資料內容隱藏起來。
<ul class="list-group list-group-flush text-left">
<li class="list-group-item" v-for="(item, key) in filteredTodos" @dblclick="editTodo(item)">
<div class="d-flex" v-if="item.id !== cacheTodo.id">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<input type="text" class="form-control" v-if="item.id === cacheTodo.id">
</li>
<!-- <li class="list-group-item">
<input type="text" class="form-control">
</li> -->
</ul>
5. 將相關事件加入 input
使用
v-model綁定text與cacheTitle紀錄編輯中的內容。使用
@keyup.esc="cancelEdit()"取消編輯,並回到methods裡面新增cancelEdit function,
並且將空物件指向給 this.cacheTodo 即為: this.cacheTodo = {}; 。
因此當使用者按下
ESC就會把預存的資料清空,清空後達成v-if="item.id !== cache.id",因此會顯示原本的div,並把input隱藏起來
- 使用
@keyup.enter="doneEdit(item)"將編輯內容存進去,並回到methods裡面新增doneEdit function,將item的
title 指向剛才預存的標題。另外預存的標題 this.cacheTitle 清空,最後將 this.cacheTodo = {}; 替換回來。
doneEdit(item)這邊,因為在編輯時會更改cacheTitle原本預存的資料,所以把this.cacheTitle指向給item.title,把原本的title更改為使用者想更改的title,只有變動title,id、completed不會變動,之後把this.cacheTitle的值清空,因為不需要了已經把他丟給原本的item.title,並且也把this.cacheTodo清空,這樣才會達到v-if="item.id !== cache.id",把原本的div顯示出來
<ul class="list-group list-group-flush text-left">
<li class="list-group-item" v-for="(item, key) in filteredTodos" @dblclick="editTodo(item)">
<div class="d-flex" v-if="item.id !== cacheTodo.id">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(key)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<input type="text" class="form-control" v-if="item.id === cacheTodo.id" v-model="cacheTitle" @keyup.esc="cancelEdit()" @keyup.enter="doneEdit(item)">
</li>
<!-- <li class="list-group-item">
<input type="text" class="form-control">
</li> -->
</ul>
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
cacheTodo: {},
cacheTitle: '',
visibility: 'allItem',
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (key) {
this.todos.splice(key, 1)
},
editTodo: function (item) {
console.log(item);
this.cacheTodo = item;
this.cacheTitle = item.title;
},
cancelEdit: function () {
this.cacheTodo = {};
},
doneEdit: function (item) {
item.title = this.cacheTitle;
this.cacheTitle = ''; //預存的標題清空
this.cacheTodo = {};
}
},
computed:{
filteredTodos: function () {
if (this.visibility == 'allItem') {
return this.todos;
} else if (this.visibility == 'processing') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'done') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
}
}
});
</script>
刪除項目補充說明
之前實做刪除時,是採用傳入陣列 index、並刪除所傳入 index 元素,但在 filtered 後的陣列中內容元素所在 index 與之
在原始陣列中的 index 有異。如果直接刪除所傳入 index 會出現誤刪的情況,因此改利用傳入 id 反查該筆資料在原始陣列
中正確的位置,然後在進行刪除。
課程中在實做時方法有二
- 一是使用
array.forEach,當遇到id同時就紀錄下目前的index,最後在刪除所紀錄下的index:
首先在 removeTodo function 裡先宣告 var newIndex = ''; 等於一個空的值,並且透過 forEach 的方式來確保我們取得的是
相同的值,這邊原本傳入的是 key ,我們把它改為是 todo 本身,在 html 程式碼 button 也需要調整,
在 @click="removeTodo(key)" 原本傳入的是 key ,改為 item 本身,變成 @click="removeTodo(item)" 。
再回到 removeTodo function ,宣告 var vm = this , vm.todos.forEach(function(item, key){}) ,這邊的 key 是等
一下我們要使用的正確位置,而 item 用來比對所有的 id 有沒有和 todo 的 id 是一樣的。
再來 forEach 裡面加入判式 if (todo.id === item.id) {newIndex = key;} 並且將 newIndex 替換至
this.todos.splice(key, 1) 的 key ,所以變成 this.todos.splice(newIndex, 1)
這邊跑 for 迴圈的目的是為了確認我們要刪除的物件 todo 跟所有物件的 id 是要相符合的,如果式符合的狀態就把 key 值
索引位置取出來,並且放到要刪除的位置上
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
cacheTodo: {},
cacheTitle: '',
visibility: 'allItem',
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: 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)
},
editTodo: function (item) {
console.log(item);
this.cacheTodo = item;
this.cacheTitle = item.title;
},
cancelEdit: function () {
this.cacheTodo = {};
},
doneEdit: function (item) {
item.title = this.cacheTitle;
this.cacheTitle = ''; //預存的標題清空
this.cacheTodo = {};
}
},
computed:{
filteredTodos: function () {
if (this.visibility == 'allItem') {
return this.todos;
} else if (this.visibility == 'processing') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'done') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
}
}
});
</script>
<ul class="list-group list-group-flush text-left">
<li class="list-group-item" v-for="(item, key) in filteredTodos" @dblclick="editTodo(item)">
<div class="d-flex" v-if="item.id !== cacheTodo.id">
<div class="form-check">
<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 }}
</label>
</div>
<button type="button" class="close ml-auto" @click="removeTodo(item)" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<input type="text" class="form-control" v-if="item.id === cacheTodo.id" v-model="cacheTitle" @keyup.esc="cancelEdit()">
</li>
<!-- <li class="list-group-item">
<input type="text" class="form-control">
</li> -->
</ul>
第二種方法使用
findIndex:findIndex是比較簡單找到索引的方式,它會把回傳為true的索引位置存到前方的變數newIndex裡
<script>
var app = new Vue({
el: '#app',
data: {
newTodo: '', // 新增代辦事項
todos: [ // 紀錄代辦事項有哪些內容
{
id: '123',
title: '妳好',
completed: false
}
],
cacheTodo: {},
cacheTitle: '',
visibility: 'allItem',
},
methods: { //新增代辦事項到 list
addTodo: function () {
var value = this.newTodo.trim();
var timestamp = Math.floor(Date.now());
if (!value) {
return;
}
this.todos.push({
id: timestamp,
title: value,
completed: false
});
this.newTodo = '';
},
removeTodo: function (todo) {
// var newIndex = '';
var vm = this;
var newIndex = vm.todos.findIndex(function (item, key) {
return todo.id === item.id;
})
this.todos.splice(key, 1)
},
editTodo: function (item) {
console.log(item);
this.cacheTodo = item;
this.cacheTitle = item.title;
},
cancelEdit: function () {
this.cacheTodo = {};
},
doneEdit: function (item) {
item.title = this.cacheTitle;
this.cacheTitle = ''; //預存的標題清空
this.cacheTodo = {};
}
},
computed:{
filteredTodos: function () {
if (this.visibility == 'allItem') {
return this.todos;
} else if (this.visibility == 'processing') {
var newTodos = [];
this.todos.forEach(function (item) {
if (!item.completed) {
newTodos.push(item);
}
})
return newTodos;
} else if (this.visibility == 'done') {
var newTodos = [];
this.todos.forEach(function (item) {
if (item.completed) {
newTodos.push(item);
}
})
return newTodos;
}
}
}
});
</script>