iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Modern Web

淺入淺出 Rails with Vue系列 第 29

【Day 29】淺入淺出 Rails with Vue - Vue 的基本概念 - 28

  • 分享至 

  • xImage
  •  

前言

本系列將介紹 Rails with Vue 的基本概念,並且以一個簡單的專案 Todo 來說明如何使用 Rails with Vue。我將透過這一系列的文章記錄我學習的過程,並且將我所學到的知識分享給大家。

Custom Events

Customizing Component v-model

在 Vue 中,v-model 是一個特殊的指令,它可以在表單元素上建立雙向綁定,也可以在自訂元件上建立雙向綁定。
默認情況下,v-model 在自訂元件上,會把 value 作為 prop 並把 input 作為事件,
但是有時候我們可能想要把 v-model 用在一個不同的 prop 和事件上,
像是 input types 為 checkboxes 或是 radio buttons 的時候,
會將 value attribte 用在不同的目的上。
這時候我們可以使用 model 這個選項來自訂 v-model 的行為。
如以下範例:

Vue.component('base-checkbox', {
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    checked: Boolean
  },
  template: `
    <input
      type="checkbox"
      v-bind:checked="checked"
      v-on:change="$emit('change', $event.target.checked)"
    >
  `
})

當我們在這個 component 使用 v-model 時,lovingVue 會傳給 checked prop,
input 的狀態改變時,change 事件會被觸發,並更新 lovingVue 的值。

<base-checkbox v-model="lovingVue"></base-checkbox>

特別注意我們還是需要在 component 的 props 中宣告 checked prop

Binding Native Events to Components

在 Vue component 中,我們也許會想要監聽 component 的 root element 上的原生事件,
像這樣的 use case,我們可以使用 .native 修飾符來監聽原生事件。
例如以下範例中,我們在 base-input component 中監聽了 focus 事件,

<base-input v-on:focus.native="onFocus"></base-input>

像這樣的寫法有時候是方便的,但如果我們監聽了一個特別的 element 像是 input 可能就不是那麼合適,
舉例來說,假如 root element 是一個 <label> element,那麼 <base-input> component 可能需要被 refactor

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

在這種情況下,在 parent 中的 .native listener 就不會被觸發了,因為 <input> element 不是 root element,而且也不會有任何的錯誤訊息,
這時候我們可以使用 $listeners 這個 property 來把所有的 listeners 傳給 root element。

{
  focus: function (event) { /* ... */ }
  input: function (value) { /* ... */ },
}

使用 $listeners 這個 property,你可以透過 v-on="$listeners" 將所有的 listeners forward 給指定的 child element,
像是上面的例子中,我們想要將所有的 listeners forward 給 <input> element,並且想要透過 v-model 來建立雙向綁定,
我們可以透過先前提到的 computed property 在 component 中 merge 這些 listeners,
並且透過 v-on 來建立雙方綁定。

Vue.component('base-input', {
  inheritAttrs: false,
  props: ['label', 'value'],
  computed: {
    inputListeners: function () {
      var vm = this
      // `Object.assign` merges objects together to form a new object
      return Object.assign({},
        // We add all the listeners from the parent
        this.$listeners,
        // Then we can add custom listeners or override the
        // behavior of some listeners.
        {
          // This ensures that the component works with v-model
          input: function (event) {
            vm.$emit('input', event.target.value)
          }
        }
      )
    }
  },
  template: `
    <label>
      {{ label }}
      <input
        v-bind="$attrs"
        v-bind:value="value"
        v-on="inputListeners"
      >
    </label>
  `
})

現在這個 <base-input> component 就是一個 fully transparent wrapper,使用起來就像一般的 <input> element 一樣,
重點是也不需要 .native 修飾符。

Reference


上一篇
【Day 28】淺入淺出 Rails with Vue - Vue 的基本概念 - 27
下一篇
【Day 30】淺入淺出 Rails with Vue - Vue 的基本概念 - 29
系列文
淺入淺出 Rails with Vue30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言