iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
Modern Web

Vue.js 進階心法系列 第 25

表單: 處理物件型資料的畫面

  • 分享至 

  • xImage
  •  

以一個這樣的表單為例

  <UserForm
    :data="data"
    @update:firstName="$store.commit('user', $event)"
    @update:lastName="$store.commit('user', $event)"
    @update:email="$store.commit('user', $event)"
    @submit="onSubmit"
  ></UserForm>

data 會是以下面的 JSON 進行的設計

{
  firstName: '',
  lastName: '',
  email: ''
}

按照前一篇文章介紹的邏輯,我們可以自由的決定「哪個欄位不要有寫入的權限」
透過改變 event 的欄位,決定相對應的欄位 disabled (其實也有其它的做法)

如下圖的三種改變

為什麼這樣設計?

若用 v-model

  <UserForm
    :data="data"
    @update:firstName="$store.commit('user', $event)"
    @update:lastName="$store.commit('user', $event)"
    @update:email="$store.commit('user', $event)"
    @submit="onSubmit"
  ></UserForm>

每一個 event 後面都是 $store.commit('user', $event) 這個是不是可以合併成相同的 event 反正後面接的語法都一樣,應該可以合併吧?而且 $event 用一個物件當輸入、用物件當輸出,怎麼不用 v-model 就好了?

  <UserForm
    :value="data"
    @input="$store.commit('user', $event)"
    @submit="onSubmit"
  ></UserForm>

可以,其實可以裡面只要這樣寫,就可以用 :value + @input = v-model
每個 input 都這樣 @input="$emit('update:user') 觸動相同的 event 就可以做成 v-model

  <UserForm
    v-model="data"
    @submit="onSubmit"
  ></UserForm>

src/components/UserForm.vue

component 裡面可以這樣實現 v-model 的做法

<input type="text"
  :disabled="!$listeners['input']"
  :value="value.firstName"
  @input="$emit('input', {
    ...value,
    firstName: $event.target.value
})">
<input type="text"
  :disabled="!$listeners['input']"
  :value="value.lastName"
  @input="$emit('input', {
    ...value,
    lastName: $event.target.value
})">
<input type="email"
  :disabled="!$listeners['input']"
  :value="value.email"
  @input="$emit('input', {
    ...value,
    email: $event.target.value
})">

缺點:

  • 若想要各別變化不同的讀寫權限,就要加上其它的 props

若各別輸出不同的值,而不是輸出物件

如果,不要物件進,物件出,而是讓各別的 event 吐出各別的值,那不就更接近原本的 form 表單控制?

src/components/UserForm.vue

那麼 component 裡面要這樣寫,就直覺多了。

<input type="text"
  :disabled="!$listeners['update:firstName']"
  :value="data.firstName"
  @input="$emit('update:firstName', $event.target.value">
<input type="text"
  :disabled="!$listeners['update:lastName']"
  :value="data.lastName"
  @input="$emit('update:lastName', $event.target.value">
<input type="email"
  :disabled="!$listeners['update:email']"
  :value="data.email"
  @input="$emit('update:email', $event.target.value">

外面就得這樣用,將組出新 object 的責任放在外面,並且要用 immutable 的做法將 mutation 裡的值換掉,以確保會更新畫面。

  <UserForm
    :data="data"
    @update:firstName="$store.commit('user', {
      ...data, firstName: $event })"
    @update:lastName="$store.commit('user', {
      ...data, lastName: $event })"
    @update:email="$store.commit('user', {
      ...data, email: $event })"
    @submit="onSubmit"
  ></UserForm>

優點: 可以個別的控製各個欄位的讀寫權限
缺點: 使用表單時,要猜一下這次拿到的 $event 是什麼。或者要印出來看一下才可以確定

最終設計

以補習班記口訣的方式來教學的,表單的 component 的設計,把握下列幾個重點。

  1. 當作是 input 的 v-model 的概念一樣,要有進有出,做成 pure component。
  2. 什麼型別進,就什麼型別出,不要有懸念,除非遇到更新照片這種特別的情況。
  3. 有多少欄位,就做多少的 event 不要多也不要少,除兩個欄位指的是同一件事,才可以共用 event
  4. 盡可能的不要加入多餘的 props,只需要傳入物件。

原因在於,想要在 component 裡面控制各別的欄位寫入 JSON,並且傳出組好的新物件,也就不用在後面的資料流判斷這個欄位是屬於哪個物件,這種多餘的判斷或資料動態分流的邏輯。

<input type="text"
  :disabled="!$listeners['update:firstName']"
  :value="data.firstName"
  @input="$emit('update:firstName', {
    ...data,
    firstName: $event.target.value
})">
<input type="text"
  :disabled="!$listeners['update:lastName']"
  :value="data.lastName"
  @input="$emit('update:lastName', {
    ...data,
    lastName: $event.target.value
})">
<input type="email"
  :disabled="!$listeners['update:email']"
  :value="data.email"
  @input="$emit('update:email', {
    ...data,
    email: $event.target.value
})">

上一篇
新增表單/編輯表單,共用?或分開?
下一篇
表單處理 Object 裡的 Object
系列文
Vue.js 進階心法30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言