iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 13
0
Modern Web

30天手把手的vue.js教學!系列 第 13

2020it邦鐵人賽-30天手把手的Vue.js教學 Day13 - 認識emit客製事件,由內向外的資料傳遞

tags: Vue.js ItIron2020

前言

昨天我們了解到在vue中,父層如何利用props屬性傳遞資料給子層,藉此讓我們建立的組件能有預期的輸出結果。既然有上到下的資料傳遞,自然也會有下到上的溝通方式,這就是我們今天要談的emit客製化事件! 這兩天的內容稍微有些困難,時間許可的話我建議跟著範例玩幾次,實際操作後會去兩者如何溝通會更有概念! 我們馬上開始吧!

什麼是emit

如果你有接觸過node.js,那我想你對於emit & on 的概念一定不陌生,在vue中也是基於完全一樣的概念來實現的。簡單的來說,emit可以讓你你建立一個全新事件,這個事件就像是我們常見的click、load、change等js事件;而on則是監聽到該事件時,你該做些什麼,我們先看一下簡單的概念範例。假設我今天想建立一個新事件叫做...delete-todo

子層(todo-list)
this.$emit('delete-todo') // 向上層傳第一個叫做delete-todo的事件

父層
<todo-list v-on:delete-todo="負責處理這事件的函數"></todo-list>

methods: {
  負責處理這事件的函數() {
    // 略
  }
}

上方的範例可以讓你了解今天想達成的全貌,你可以發現這就像是一般的事件監聽處理,只是這個事件是來自子層的客製事件罷了! 沒有這麼複雜難懂,放心?
image alt

emit基本語法

若你今天沒有要傳遞資料,只是單純的想通知父層現在有個客製化事件觸發了,記得去處理,那你可以採用剛剛示範的寫法。

this.$emit('delete-todo')

當然,很多時候我們會碰到要把子層的資料傳回父層的寫法,emit是可以接收第二個參數作為你想傳遞的資料,假設我今天想回傳title這個資料,寫法就會改為以下。

this.$emit('delete-todo', {
  title: 'Some title'  
})

真的相當簡單~沒騙你的!

在todo-list組件內加入客製事件

我們接續昨天的範例,今天我們要讓那兩個按鈕生效! 讓刪除與完成代辦事項的功能可以順利執行。

等等等等等一下,為什麼我們一定要透過emit傳遞事件呢? 不能直接改嗎?

這個問題問得很好,原因在於今天我們得到的資料是來自父層,不管是要刪除資料或是更新資料(那個todo)都只能在父層操作,子層並沒有辦法直接影響父層的資料。 實際上的資料流動會是這樣的概念

     props傳遞資料
    ------------> 
父層              子層
    <------------
    emit傳達客製事件

當我今天在子層點擊了刪除按鈕,觸發了我客製的事件delete-todo,父層監聽到delete-todo被觸發後,執行對應的處理函數,最終在父層刪除todos裡面的資料,而資料更新後會重新render,讓我們看到最後刪除的結果。

了解概念後我們馬上開始實作,首先請你在todo-list的組件中加入一個methods,我們就叫它deleteTodo吧!在這個函數裡面,我們要利用emit傳遞客製事件與資料給父層(給title讓父層知道要刪除哪一個) 同時利用v-on:click將它掛到垃圾桶的icon上面,表示點擊後要觸發這個事件。

Vue.component("todo-list", {
  template: `
    <div :class="[todo.isComplete? 'success':'error','todo-wrapper']">
      <div class="todo-title">
        {{todo.title}}
      </div>
      <div class="todo-icons">
        <i class="fa fa-check" aria-hidden="true"></i>
        <i class="fa fa-trash-alt" aria-hidden="true" @click="deleteTodo"></i> // 修改這裡
      </div>
    </div>
  `,
  props: {
    todo: {
      type: Object,
      required: true
    }
  },
  methods: {  // 加入methods屬性
    deleteTodo() {
      this.$emit('delete-todo', {
        title: this.todo.title
      })
    }
  }
});

在父層監聽子層傳來的事件

如果一開始的說明有看懂,剩下的部分你應該大概有想法了,首先我們要回到父層,在template中的子元件上監聽子層傳來的事件,並告訴它你這個事件我收到後,我打算用哪個函數來處理

<todo-list
      v-for="todo in todos"
      :key="todo.id"
      :todo="todo"
      @delete-todo="handleDeleteTodo">
</todo-list>

接著我們去下方methods的部分加入我們剛隨便寫的handleDeleteTodo函數,先用一個簡單的console確認我們是否真的收到來自子層的事件了。

export default {
  data() {
    return {
      message: "Welcome to Vue!",
      todos: todos
    };
  },
  methods: {
    handleDeleteTodo() {
      console.log("get the event!");
    }
  }
};

此時你隨意點擊垃圾桶icon,你應該可以在console中看到我們設置的訊息

emit demo

確認有收到事件後,我們就可以真正改寫父層的handle function了
請將handleDeleteTodo的內容改為以下,別忘了我們有從子層收到一包資料,它會是你handle function的第一個參數,看下方的範例應該很快就懂了

methods: {
    handleDeleteTodo(payload) {
      console.log(payload.title);
      this.todos = this.todos.filter(item => item.title !== payload.title)
    }
}

我特別將payload.title印出來,你可以發現那就是我們傳進去的參數
上方的範例當然也可以經由解構寫得更好看一點

methods: {
    handleDeleteTodo({ title }) {
      console.log(title);
      this.todos = this.todos.filter(item => item.title !== title)
    }
}

至此我們的刪除功能就可以正常運作了!! 透過以上的範例我想你們對於基本的事件傳遞也有了些許的認識,剩下的完成功能就交給你做囉:D 這裡有做完後的成品提供給你參考!

結語

我們今天認識了如何用emit從子層傳遞資料給父層處理,這樣的溝通方式在小規模的專案中會相當的實用,不過之後專案規模大一點的時候,自然會有更適合的資料管理方式,這個我們就之後再提囉! 大家明天見!

此文章同步發布於個人部落格,有興趣的大大也可以來參觀一下:D


上一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day12 - 認識props屬性,從外向內的資料傳遞
下一篇
2020it邦鐵人賽-30天手把手的Vue.js教學 Day14 - 認識插槽slot基礎用法
系列文
30天手把手的vue.js教學!30

尚未有邦友留言

立即登入留言