表单: 处理物件型资料的画面

以一个这样的表单为例

  <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
})">

<<:  [第25天]理财达人Mx. Ada-ADX指标

>>:  Day-26 修改Icon

[Day21] 扩展你的设计:根据与对话发生的装置修改对白

从手机到智慧音箱,在不同装置上要考量到的情形皆有差异。 这篇文章中将先介绍Google助理可回应的...

[从0到1] C#小乳牛 练成基础程序逻辑 Day 1 - 认识C++++

乳牛与程序人 | C#我懂你 | 工程师的小幽默 (附字幕) 🐄填写今日份随堂测验 ...

[Day6] 注册API – model之AbstractUser

各位夥伴们大家好,今天是我们进入API阶段的第一天,在撰写API的逻辑之前,我们需要先到user\m...

资料库练习

这次练习沿用的程序码是我铁人赛每篇阅览数。先说一下程序码的改动,原本我只抓阅览数,那这次有多增加发文...

【Day10】表单 Form:受控元件 Controlled Component

在 React 中,允许直接用 HTML 来建立表单, 但使用 JavaScript functio...