第12屆鐵人賽Day 11 Vue元件單向傳遞: $emit 製作todo list

經過昨天介紹$emit可以傳遞自訂的事件給父層元件之後,今天來利用$emit的特性,設計一個3M便利貼風格的to-do list吧!

實作需求:

  • 新增
    我想要在input欄位裡,讓使用者從Todos元件輸入本日代辦事項, 按完enter送出後,把資料送去Root元件裡顯示。

  • 刪除
    點擊任一的todo item,就可以把item從todo陣列裡移除。

Step0. Todos元件:設計template

input form綁上雙向事件處理的v-on,讓使用者enter送出後呼叫createItem這個methods裡面的function。

Step1. Todos元件的methods: $emit('自訂事件名稱')傳出事件

createItem($event)方法裡面帶了參數,讓我們可以把$event.target.value傳到上層的Vue Instance。

Todos.vue

<template id="todo-3M">
  <div class="todo-form">
      <label>本日To do list</label><i class="fas fa-pencil-alt" ></i><hr>
      <input class="todo-input" type='text' @keyup.enter="createItem" placeholder="請輸入您今天的代辦事項">
  </div>
</template>

<script>
  export default {              
    methods: {
      createItem($event){
        this.$emit("additem", $event.target.value)
        $event.target.value = ""
      }
    },
    template: "#todo-3M"
  }
</script>

<style scoped>
<!-- 略 -->
</style>

Step2. 在Vue Root掛載點,接收傳出來的事件

Vue Root掛載點內的<Todos>元件,綁上雙向事件處理的v-on, 讓<Todos>元件傳遞出來的additem可以跟Root元件的addMoreItem綁定

home.html.erb

<div id="content">
  <div class="main-title">Vue.js x Rails第12屆鐵人賽專案:</div>
  <div class="main-body">本日主題:</div>

  <Todos @additem="addMoreItem"></Todos>
  <ul class="todo-item">
    <li v-for="todo in todos" @click="removeItem(todo)"><i class="far fa-calendar-check"></i>  </li>
  </ul>
  
  <Foot></Foot>
</div>

Step3. 父元件接收事件後,執行addMoreItem() function

我的todos陣列初始值預設有三件代辦事項。父元件接收事件之後,還可以再把其他代辦事項push進去這個陣列。

Step4. v-for裡的todo迴圈,使用@click事件綁定removeItem(todo) function

若對單一todoitem點擊,則透過javascript的splice方法,把該項todo item從陣列中移除。

home.js

import TurbolinksAdapter from 'vue-turbolinks'
import Vue from 'vue/dist/vue.esm'
import Todos from "../components/todos"
import Foot from "../components/foot"


Vue.use(TurbolinksAdapter)

document.addEventListener('turbolinks:load', () => {
  let el = document.querySelector("#content");

  if (el){
    new Vue({
      el,
      data: {
        day: "第 11 天",
        topic: "元件的由內而外傳遞事件 - emit",
        todos: ["買咖啡", "買口罩", "去郵局"],
      },
      methods: {
        addMoreItem(item){
          this.todos.push(item)
        },
        removeItem(item){
          let itemIndex = this.todos.indexOf(item)
          if (itemIndex >= 0){
            // 從array中刪除項目,並回傳被刪除後的項目
            this.todos.splice(itemIndex, 1)
          }
        }
      },
      components: { Todos, Foot }
    })    
  }
})

完成圖:

如果非父子元件之間傳遞,而是平行階層、或是跨階層的元件資料傳遞, 我們會透過Vue的event bus來實作,或者是以Vuex進行狀態管理。 這就是明後兩天要介紹的部份囉~請待下回分解!

Ref: