iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 20

Day 20. 新鮮好吃的手做 v-model

  • 分享至 

  • xImage
  •  

Vue component 中 :bindv-model 分別是「單向資料流」以及 「雙向綁定」,在單向資料流當中,有時候我們會需要將 Vue component 中的資料或狀態回傳出來,所以需要透過自訂事件的方式,通知上層的 component 接收。更進一步,有時候我們希望帶進 component 的參數可以像 <input> 一樣,只要有變動、大家都會跟著調整。因此接下來就會用「搜尋」逐一說明「自訂事件」和「客製 v-model」。

自訂事件

常見的「搜尋」會包括「搜尋欄位」和「關鍵字」兩個部分,基本建置如下面步驟:

  1. 建立 SearchBar.vue,保留兩個通知,分別是「有搜尋資料要進行搜尋」以及「清除搜尋資料」:
<template>
  <div>
    <select v-model="searchColumn">
      <option v-for="(column, index) in searchColumns" :key="index"
              :value="column['value']">
        {{column['label']}}
      </option>
    </select>
    <input v-model="keyword" @keyup.enter="search"/>
  </div>
</template>

<script>
  export default {
    name: "SearchBar",
    props: {
      searchColumns: {
        type: Array,
        required: true,
      },
    },
    data() {
      return {
        searchColumn: this.searchColumns[0].value,
        keyword: undefined,
      }
    },
    watch: {
      searchColumn() {
        this.search();
      },
    },
    methods: {
      search() {
        if (this.validSearch()) {
          // 通知上層 component 要搜尋,並回傳搜尋資訊
        } else if (!this.keyword) {
          // 通知上層 component 清除搜尋
        }
      },
      validSearch() {
        const hasSearchColumn = !!this.searchColumn;
        const hasKeyword = !!this.keyword;
        return hasSearchColumn && hasKeyword;
      },
    }
  }
</script>
  1. 撰寫通知 this.$emit(<事件名稱>, <回傳資料>)
      search() {
        if (this.validSearch()) {
          const searchData = {
            column: this.searchColumn,
            keyword: this.keyword
          };
          this.$emit('on-search', searchData);
        } else if (!this.keyword) {
          this.$emit('on-clear');
        }
      },
  1. 使用 componet:
<template>
  <search-bar :search-columns="user.searchColumns"></search-bar>
</template>

<script>
  import SearchBar from "./SearchColumn";
  export default {
    name: "test4",
    components: { SearchBar },
    data() {
      return {
        user: {
          searchColumns: [
            { value: 'id', label: 'ID' },
            { value: 'name', label: '姓名' },
            { value: 'email', label: '信箱' },
          ],
        }
      };
    },
  }
</script>
  1. 透過 v-on 綁定要執行的方法、接收通知。可以注意到 toSearchclearSearch 兩個方法,如果有回傳資料再建立參數、並由 Vue 框架自動注入:
<template>
    <search-bar
      :search-columns="user.searchColumns"
      @on-search="toSearch"
      @on-clear="clearSearch">
  </search-bar>
</template>
    methods: {
      // 有回傳資料再加上參數即可
      toSearch(searchData) {
        console.log(searchData);
      },
      clearSearch() {
        console.log('search cleared');
      }
    }

小結上面的結果,只要透過 this.$emitv-on: 兩者之間的搭配,就可以達到客製事件。

客製 v-model

我們可以看到每次要進行搜尋時,都會回傳資料,所以其實可以透過 v-model 綁定整個 { column, keyword },這樣上層跟下層之間就可以直接連動。使用 v-model 的改寫步驟如下:

  1. props 中加入 value 參數,注意就名稱是固定用法:
    props: {
      value: {
        type: Object,
        required: true,
      },
      // ...
    }
  1. 修改 data 資料,順帶一提,props 的參數不可以綁在 v-model,要改成 data 或是 computed 資料:
    data() {
      return {
        searchColumn: this.value.value || this.searchColumns[0].value,
        keyword: this.value.keyword,
      }
    },
  1. 修改事件名稱為 input,注意!名稱是固定用法:
      search() {
        if (this.validSearch()) {
          const searchData = {
            column: this.searchColumn,
            keyword: this.keyword
          };
          // 從原本客製事件名稱改為「input」
          this.$emit('input', searchData);
        } else if (!this.keyword) {
          this.$emit('on-clear');
        }
      },
  1. 上層 component 改用 v-model 綁定
<template>
  <search-bar
      v-model="user.searchData"
      :search-columns="user.searchColumns" @on-clear="clearSearch">
  </search-bar>
</template>

<script>
  import SearchBar from "./SearchColumn";
  export default {
    name: "test4",
    components: { SearchBar },
    data() {
      return {
        user: {
          // ...
          // 增加作為 v-model 使用的資料
          searchData: {
            column: 'name',
            keyword: undefined,
          }
        }
      };
    },
  }
</script>

改寫過後,當我們使用 watch 監控 user.searchData,可以看到可以達到原本客製事件效果。 小結上面改寫結果,利用 value 參數 和 input 事件名稱就可以做到 v-model!


無論是客製事件或是 v-model 雙向綁定,都讓 component 的使用更靈活也更方便。 Vue component 基本使用介紹完之後,接著我們就要來看 Nuxt 的 page 有哪些新功能啦!


上一篇
Day 19. 啊啊啊 v-for 壞掉惹!!
下一篇
Day 21. Nuxt page component 粉墨登場
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言