# 27. TodoList 頁面模組 3 - 修改
這個小節主要說明「修改事項」模組。
# 修改事項的處理細節
修改事項主要會有這幾項功能:
- 雙擊
<label>
可編輯內容。 - 寫入:鍵盤按下 Enter 會儲存編輯後的結果
- 取消:鍵盤按下 Esc 或滑鼠在輸入框外點擊 (blur),可取消編輯。
這裡的修改事項內容,和上面修改完成狀態的寫法不一樣:
- 修改狀態:修改後直接
commit
到 store 裡面。 - 修改內容:會先判斷要寫入或取消,才去改 store 裡面的資料。
# 修改 template
首先,我們可以依據想要的功能修改 template
的結構,然後再去 JS 寫邏輯:
- 在
label
綁定雙擊事件,使得 "TodoListItem" 顯示input
輸入框,以供使用者編輯資料。 - 建立一個新的
input
輸入框,並帶有以下屬性和值:v-model.trim="edit"
:雙向綁定data
中的edit
,存放輸入框的內容並去除首尾空白。v-if="edit !== null"
:如果edit
不是null
,就會顯示這個輸入框。v-focus
:畫面出現這個輸入框時,滑鼠游標會自動 focus。@keyup.enter="submitHandler"
:按下 Enter 寫入內容。@keyup.esc="cancelHandler"
:按下 Esc 取消編輯。@blur="cancelHandler"
:滑鼠點擊輸入框以外的地方取消編輯。
- 編輯事項的時候,我們會以一個
input
輸入框取代原有的input+label+button
結構。我們可以用一個<template>
標籤將input+label+button
包起來,一來不會多一層 DOM,二來便於使用v-else
一起判斷是否要呈現到畫面上。
//- components/TodoListItem/template.html
<div class="todoListItem">
<input
type="text"
class="edit"
v-model.trim="edit"
v-if="edit !== null"
v-focus
@keyup.enter="submitHandler"
@keyup.esc="cancelHandler"
@blur="cancelHandler"
/>
<template v-else>
<input type="checkbox" class="toggle" v-model="complete" />
<label @dblclick="editHandler">{{ todo.content }}</label>
<button class="destroy" @click="destroyHandler"></button>
</template>
</div>
# 雙擊編輯內容
- 建立
edit
暫存輸入框裡的資料,並預設值為null
。- 如果預設值為空字串,且輸入框的顯示條件為
v-if="edit"
。在編輯過程中將輸入框的內容清空時,輸入框會因為v-if="edit"
的判斷而切換回來。
- 如果預設值為空字串,且輸入框的顯示條件為
- 當
editHandler
被觸發後,將 store 中todo
的內容傳給edit
- 此時
edit
的值不是null
,畫面上會將此事項改為輸入框,並帶入事項內容以供編輯。
//- components/TodoListItem/index.vue
// 不相關的其他內容省略
export default {
data(){
return {
edit: null
}
},
computed: {
todo(){
return this.$store.state.todos[this.index]
},
},
methods: {
editHandler(){
this.edit = this.todo.content
},
}
# 取消編輯內容
取消編輯比較單純,只要讓 this.edit
的值是 null
,輸入框就會因 v-if
的判斷而切回 input+label+button
。
//- components/TodoListItem/index.vue
cancelHandler(){
this.edit = null
}
由於 label
是拿 store 裡面 todos
的值,而這個值沒有被改動,因此畫面切換回來的時候,內容會維持原樣。
# 寫入編輯內容
- 判斷是不是空字串
- 如果是空字串,我們就刪除這筆資料。
- 更新資料內容
- 如果不是空字串,就將
edit
的值commit
到 store 裡面。
- 如果不是空字串,就將
- 清除
this.edit
的值- 最後要將
this.edit
的值恢復成null
,畫面才會切回來顯示事項。
- 最後要將
//- components/TodoListItem/index.vue
submitHandler(){
if(!this.edit) return this.destroyHandler()
this.$store.commit("UPDATE_TODO", {
index: this.index,
data: {
content: this.edit,
complete: this.todo.complete
}
})
this.cancelHandler()
},
# 改寫 UPDATE_TODO
前面資料觀測的單元有提到,如果直接對陣列中的資料作修改,Vue 無法觸發 Observer 通知資料變動,畫面就不會即時更新。因此 Vuex store 裡面的 UPDATE_TODO
要改寫:
//- store/index.js
mutations: {
UPDATE_TODO(state, { index, data }) {
// state.todos[index] = data
state.todos[index].complete = data.complete
state.todos[index].content = data.content
LS.save(state.todos)
},
},