# 25. TodoList 頁面模組 1 - 新增

在這個範例中,畫面可分為三個模組:

  • 「新增資料」模組
  • 「列表顯示」模組
  • 「修改事項」模組

這個小節主要說明「新增資料」的模組,也就是 input 輸入框。

# 畫面初始化

在建立模組之前,我們可以先在專案進入口 "App.vue" 進行初始化:

  • mounted 階段載入資料。
  • 透過 computed 拿到資料的索引值。
//- App.vue

<script>
export default {
  computed: {
    todoIndex() {
        return this.$store.getters["todoIndex"];
    }
  },
  mounted() {
    this.$store.dispatch("INIT_TODOS");
  },
}
</script>

# 輸入框的處理細節

要實現新增功能,需要先有輸入框讓使用者輸入內容。而處理輸入框的內容時,須注意這幾個細節:

  • 去除首尾空白
  • 判斷是否為空值
  • 清空輸入框

# 建立「新增資料」模組

在 "components" 資料夾建立 "TodoInput" 資料夾,並在裡面新增以下檔案:

  • index.vue
  • template.html
  • style.css (視需求,如果 CSS 樣式另外管理,則無需在此建立樣式檔案)

# template.html

  • 主要內容為 input 輸入框,但建議在最外層加上一個 div,確保最外層只有一個元素。
  • input 輸入框有幾個要點:
    • 使用 v-model 雙向綁定資料 todo,並用 trim 去除空白。
    • 鍵盤按下 "Enter" 後,即可觸發事件以新增事項。
//- components/TodoInput/template.html

<div class="todoInput">
  <input
    type="text"
    placeholder="請輸入待辦事項"
    v-model.trim="todo"
    @keyup.enter="submitHandler"
  />
</div>

# index.vue

  • data 中建立 todo 暫存輸入框的內容 (雙向綁定)。
  • 建立新增事項的處理器:
    • 先去除首尾空白
      • 可以在 templatev-model 後方加上修飾詞 .trim
    • 判斷是否為空值
      • 如果去除空白之後為空值,就 return
    • 推送資料到陣列
      • 如果有值,就 commit 觸發 ADD_TODO ,將資料推送到 store 裡的 todos 陣列。
      • 資料應包含:
        • 輸入框的 todo 內容
        • 預設事項 complete: false
    • 清空輸入框內容
//- components/TodoInput/index.vue

<script>
export default {
  data(){
    return {
      todo: ""
    }
  },
  methods: {
    submitHandler(){
      if(!this.todo) return
      this.$store.commit("ADD_TODO", {
        content: this.todo,
        complete: false
      })
      this.todo = ""
    }
  },
}
</script>

<template src="./template.html"></template>

# 使用「新增資料」模組

建立好「新增資料」的模組之後,要回到 "App.vue" 引入、掛載才能使用。

  • 引入:將模組 import 進來。
  • 掛載:增加 components 並放入模組名稱。
  • 使用:到 "template" 使用模組。
//- App.vue

<template>
  <div id="nav">
    <router-link :to="{name: 'all'}">全部</router-link> |
    <router-link :to="{name: 'active'}">未完成</router-link> | 
    <router-link :to="{name: 'complete'}">已完成</router-link>
  </div>
  <TodoInput/> // 使用模組
</template>

<script>
// 引入模組
import TodoInput from "./components/TodoInput.vue/index"

export default {
  components: { // 掛載模組
    TodoInput
  },
  // 略 ...
}
</script>

# directive 讓游標 focus 在輸入框

如果希望優化使用者體驗,讓網頁載入後,滑鼠游標直接 focus 在輸入框,而不用透過使用者滑到那裡點擊,可以搭配 Vue 的 directive 自定義指令,讓我們在特定時間點直接操作 DOM。

# 註冊指令

Vue 的自定義指令可註冊於全局或模組,這個範例我們會註冊於全局使用。

由於我們需要在 mounted 後 focus 到輸入框,正好可在 "main.js" 使用官網提供的範例程式碼註冊指令:

//- main.js

const app = Vue.createApp({})
// 注册一个全局自定义指令 `v-focus`
app.directive('focus', {
  // 当被绑定的元素挂载到 DOM 中时……
  mounted(el) {
    // 聚焦元素
    el.focus()
  }
})

在上面的程式碼中,

  • 第一個參數 focus 是這個指令的名稱。
  • 第二個參數中,可使用 hook 決定操作的時間點 (例如 mounted), el 則是被綁定的 DOM 元素。

# 使用指令

v-modelv-bind 相似,自定義指令都會有 v- 前綴。因此使用時,在要使用此指令的 DOM 元素加上 v-focus,該 DOM 元素就能被操作。

//- components/TodoInput/template.html

<div class="todoInput">
  <input
    type="text"
    placeholder="請輸入待辦事項"
    v-model.trim="todo"
    @keyup.enter="submitHandler"
    v-focus // 加上 v-focus 操作 DOM
  />
</div>

若有需要,使用自定義指令的時候也能傳參數。但這裡的 v-focus 不用傳參數。