表单处理 Object 里的 Object

今天来看看一个常见问题。

{
  first_name: 'chris',
  last_name: 'wang',
  email: '[email protected]'
  camp: {
    name: 'web camp',
    member_count: 10
  }
}

先做已经会的

先依昨天讲的 UserForm 的 component 可以这样写。

<form @submit.prevent="$emit('submit')">
    <label>firstName<br/>
    <input type="text"
      :disabled="!$listeners['update:firstName']"
      :value="data.firstName"
      @input="$emit('update:firstName', {
        ...data,
        firstName: $event.target.value
      })">
    </label><br/>
    <label>lastName<br/>
    <input type="text"
      :disabled="!$listeners['update:lastName']"
      :value="data.lastName"
      @input="$emit('update:lastName', {
        ...data,
        lastName: $event.target.value
      })">
    </label><br/>
    <label>email<br/>
    <input type="email"
      :disabled="!$listeners['update:email']"
      :value="data.email"
      @input="$emit('update:email', {
        ...data,
        email: $event.target.value
      })">
    </label><br/>
    <pre>camp: {{data.camp}}</pre>
    <input type="submit" value="送出">
</form>

画面

还有一个 camp 怎麽办呢?

{
  // ....
  camp: {
    name: 'web camp',
    member_count: 10
  }
}

先为这个物件做一个 component

也就是以这样的视角看待它

{
  name: 'web camp',
  member_count: 10
}

有一个 camp 的视角时,依昨天教的,是不是就可以这样做

<div class="camp-fields">
  <div class="camp-fields">
    <label>
      name<br />
      <input
        type="text"
        :disabled="!$listeners['update:name']"
        :value="data.name"
        @input="
          $emit('update:name', {
            ...data,
            name: $event.target.value,
          })
        "
      />
    </label><br />
    <label>
      member_count<br />
      <input
        type="number"
        :disabled="!$listeners['update:member_count']"
        :value="data.member_count.toString()"
        @input="
          $emit('update:member_count', {
            ...data,
            member_count: Number($event.target.value),
          })
        "
      />
    </label><br />
  </div>
</div>

在此,还对 type="number" 的栏位做了转态,将资料保持在数字型别。但是显示时要转型别,从数字变成字串。

<form @submit.prevent="$emit('submit')">
    <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
      })">
    <camp-form
      :data="data.camp"
      @update:name="$emit('update:camp', {
        ...data,
        camp: $event
      })"
      @update:member_count="$emit('update:camp', {
        ...data,
        camp: $event
      })"
    ></camp-form>
  <input type="submit" value="送出">
</form>

并且在 UserForm 上面加上新的 event @update:camp="$store.commit('user', $event)" 表示在此会修改 camp 这个属性。

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

在此有一个有趣的比较

为方便抓到重点,我把不必要的 type="text" 先拿掉,因为它是预设值,也把 disabled 拿掉因为要不要这个属性,是看需求,不是设计的重点。(只是符合需求时,这样做很符合语意)

<input
  :value="data.firstName"
  @input="$emit('update:firstName', {
    ...data,
    firstName: $event.target.value
  })"
>

跟下面的 form 做一个对比。

<camp-form
  :data="data.camp"
  @update:name="$emit('update:camp', {
    ...data,
    camp: $event
  })"
  @update:member_count="$emit('update:camp', {
    ...data,
    camp: $event
  })"
></camp-form>

为型别而生!才是 web component 设计的思维

两个都是为了处理 user 的属性,而使用的 component。

输入

  • 简单型别,使用了 :value 将值输入 component
  • 复杂型别,使用了 :data 将物件输入 component

输出

  • 简单型别,使用了 @input 表示更新这个值,并将值输出 component
  • 复杂型别,使用了 @update:property 表示物件更新某个属性,并将物件输入 component

所以

在 input 的输入中,:value="data.firstName" 输出就是取得 firstName 的新值。
在 camp-form 的输入中 :data="data.camp" 输出就是取得 user.camp 的新值。

两件事情的更新层次一致之後,按照这样的观念实作,任何巢状式的物件,就不用害怕它的表单有多复杂了。

对应方式也许不是一对一,但是可以限缩在一个合理的有限范围之内,像是表单控制项与资料型别之间的关系,也有着一定的合理范围之内。

复习昨天的重点

有了昨天的学习心得。今天做出这样的结果是不是就快速许多了呢?

  • 当作是 input 的 v-model 的概念一样,要有进有出,做成 pure component。
  • 什麽型别进,就什麽型别出,不要有悬念,除非遇到更新照片这种特别的情况。
  • 有多少栏位,就做多少的 event 不要多也不要少,除两个栏位指的是同一件事,才可以共用 event
  • 尽可能的不要加入多余的 props,只需要传入物件。

<<:  计算资源及资料的设定02

>>:  Day26 NiFi 场景应用范例 (一)

DAY 19 『 连接 API 实作 - 天气 APP 』Part1

今天会分享如何连接 API 实作出天气 APP。 具体的说是 HTTP 请求天气站点的 API,取得...

系统开发生命周期(SDLC)

SDLC定义了工程系统时的阶段和过程。由於系统的多样性,它通常不提供特定的设计原则。 系统开发生命周...

30天打造品牌特色电商网站 Day.22 图片排版实作

分享完关於图片的排版设计,今天我们就来进行排版的实作啦! 之前有介绍过的<float>&...

Ruby解题分享--Sqrt(x),二分搜寻演算法。

老歌了~ 宅男开YouTube来看,永远不缺手游广告... 最近有个广告台词,开局一定要选拳法,如...

Day 24 开发者福音无服务器运算

随着资讯技术普及与推陈布新,基础设施及服务(IaaS)、平台即服务(PaaS)、软件即服务(Saa...