第12屆鐵人賽Day 28 Rails專案開發 - 刪除ticket (使用vuex狀態管理)

CRUD新增、刪除、修改、顯示功能是一個完整的線上系統不可或缺的功能。 前天我們完成了更新 ticket,以及介紹了dispatch讓Vue可以聯絡到Vuex的action, 今天就延續著介紹以Vue.js實作CRUD的刪除功能!

Step 1. UI: 刪除icon連動 deleteTicket method

我們移至ticket.vue這個ticket元件的檔案, 起手式來先用個fontawesome加一個可愛的垃圾桶,並且v-on綁定click事件deleteTicket

<template>
  <div class="ticket">
    
    <div>
      <button class="edit-btn" @click="editTicket=true"><i class="fas fa-edit text-gray-400"></i></button>
      //垃圾桶在這
      <button class="delete-btn" @click="deleteTicket"><i class="fas fa-trash text-gray-400"></i></button>

      <div v-if="editTicket" class="edit-area">
        <i class="far fa-window-close edit-cancel" @click="cancelUpdate"></i> 
        <textarea type="text" class="edit-input" v-model="ticket.name"></textarea>
        <button class="update-ticket-btn" @click="updateTicket">更新</button>
      </div>

    </div>
  </div>
</template>

垃圾桶放完後,一邊來點點看,一邊思考如何撰寫deleteTicketmethod

刪除功能最重要的是要刪到對的東西!因此,為了確定有抓對那筆即將被刪除的ticket,先用console.log確認一下是不是正確的資料:

<script>
  export default {              
    name: 'Ticket',
    props: ["ticket"],
    data: function () {
      return {
      }
    },    
    methods: {
      deleteTicket(evt){
        event.preventDefault();
        if (confirm(`確定刪除"${this.ticket.name}" 嗎?`)){
          console.log("remove: ") 
          console.log(this.ticket.id)
          console.log(this.ticket.name)
        }

      }
    }
  }
</script>

Step2. 使用Vuex狀態管理 this.$store.dispatch

昨天介紹了dispatch這個概念,讓我們在Vue元件裡可以呼叫到Vuex store instance中actions定義的方法。

請看目前程式碼的架構,左邊為Vue(ticket元件)、右邊為Vuex store。

我想要當點擊確認刪除後,能夠呼叫action中定義的deleteTicket方法,並且傳入ticket_id和column_id兩個參數。

2-1. 橘色框線:Vue元件裡的methods despatch至Vuex的action

methods: {
  cancelUpdate(evt){
    //略
  },
  updateTicket(evt){
    //略
  },
  deleteTicket(evt){
    event.preventDefault();
    if (confirm(`確定刪除"${this.ticket.name}" 嗎?`)){
      this.$store.dispatch("deleteTicket", {ticket_id: this.ticket.id, column_id: this.ticket.column_id})                
    }
  }
}

2-2. 黃色框線:Vuex actions裡的delete_column,呼叫mutations

確定刪除ticket的路徑及動詞,再請Rails打ajax到後端。

Helper HTTP Verb Path Controller#Action
kanban_ticket_path DELETE /kanbans/:kanban_id/tickets/:id(.:format) tickets#destroy

成功刪除資料庫內的ticket之後, 我們發出一個commit,請求mutation裡DELETE_TICKET重新前端渲染的資料,並帶入剛剛從Vue元件傳進來的ticket_id和column_id兩個參數。

  actions: {
    updateTicket({ commit }, {id, name}){
      //略
    },
    deleteTicket({ commit }, {ticket_id, column_id}){
      let el = document.querySelector("#column");   
      Rails.ajax({
        url: `/kanbans/${el.dataset.kanbanid}/tickets/${ticket_id}`,
        type: 'DELETE',
        dataType: 'json',
        success: result => {
          commit("DELETE_TICKET", {ticket_id, column_id});
        },
        error: error => {
          console.log(error)
        }
      });
    },
    dragColumn({ commit, state }, evt) {
      //略
    },
    fetchColumn({ commit }, kanbanid){
      //略
    }
  }

2-3. 紅色框線:Vuex mutations裡的DELETE_TICKET

DELETE_TICKET:需要透過傳進來的ticket_idcolumn_id,去比對state裡的columns的index現在是第幾欄和第幾列,再透過splice把需要刪掉的那張ticket從columns陣列中移除。

(PS. 比較mutations裡的UPDATE_TICKET可以直接透過ticket實體去找自己的id以及reference的column_id,但DELETE_TICKET已經刪除了ticket實體,因此我們要從外面的元件帶進來column_id 和ticket_id來比對資料。)

  mutations: {
    UPDATE_COLUMNS(state, columns){
      state.columns = columns;
    },
    // Day 28
    DELETE_TICKET(state, {ticket_id, column_id}){      
      let column_index = state.columns.findIndex(col => col.id == column_id)
      let ticket_index = state.columns[column_index].tickets.findIndex(tkt => tkt.id == ticket_id)
      state.columns[column_index].tickets.splice(ticket_index, 1)
    },

    //Day 27
    UPDATE_TICKET(state, ticket){
      let column_index = state.columns.findIndex(col => col.id == ticket.column_id)
      let ticket_index = state.columns[column_index].tickets.findIndex(tkt => tkt.id == ticket.id)
      state.columns[column_index].tickets.splice(ticket_index, 1, ticket)
    }
  },

感想:原本以為CRUD裡,刪除總是那個最快可以做好的功能,但我昨天和今天大概花了10個小時才完全搞清楚箇中原理,把握出vuex的流程、如何傳參數、如何送commit給mutation~(以及解完自己產生的Bug 嗚嗚)

當ticket刪得掉時候真的很感動!

透過mutations去更動前端雖然看起來有點繞(dispatch=> actions => mutations),但等到我們明天來實作Action cable,你就會發現好處多多~敬請期待明天!