Single Page Application の Form 画面における Best Practice を考える(後編)

2回に分けて Form Validation について考察しました。最後に Form Validation を子となる Component に任せるやり方を紹介します。

正直この手法は少しやりすぎな気もしているので好みの問題が出てくると思います。

## Page Component はどこまで関心を持つべきか？

さて、前回 form validation の判定を Nuxt.js でいうところの Page Component で行っていました。この validation に不備があればエラーメッセージをコントロールし、不備がなければ Submit button を押す事ができます。

これらの処理について本当に Page Component が知っているべきなのか。というのは議論の余地があると思いますが、試しに form validation のロジックを form component に任せてみましょう。

## やってみた

姓、名、セイ、メイの入力フォームは基本的に1セットだと思うのでこれらを component にしてみます。`onChange` で変更が発生するたびに fields の情報と isValid を返します。Page Componet が関心を持つのは入力された値とそれらが valid であるかどうかだけ、にしました。

```vue:components/forms/user-name-fields.vue
<template>
  <div>
    <div class="field is-horizontal">
      <div class="field-label is-normal">
        <label class="label">お名前(漢字)</label>
      </div>
      <div class="field-body">
        <InputTextField
          placeholder="姓"
          :errorMessages="errors.lastNameKanji"
          :onChange="(value) => handleChangeFields({lastNameKanji: value})"
        />
        <InputTextField
          placeholder="名"
          :errorMessages="errors.firstNameKanji"
          :onChange="(value) => handleChangeFields({firstNameKanji: value})"
        />
      </div>
    </div>
    <!-- お名前(カタカナ) -->
    <div class="field is-horizontal">
      <div class="field-label is-normal">
        <label class="label">お名前(カタカナ)</label>
      </div>
      <div class="field-body">
        <InputTextField
          placeholder="セイ"
          :errorMessages="errors.lastNameKana"
          :onChange="(value) => handleChangeFields({lastNameKana: value})"
        />
        <InputTextField
          placeholder="メイ"
          :errorMessages="errors.firstNameKana"
          :onChange="(value) => handleChangeFields({firstNameKana: value})"
        />
      </div>
    </div>
  </div>
</template>

<script>
import { validate } from '~/utils/validator'
import InputTextField from '~/components/forms/input-text-field'

export default {
  components: {
    InputTextField
  },
  props: ['onChange'],
  data () {
    return {
      fields: {},
      errors: {},
    }
  },
  methods: {
    handleChangeFields (fields) {
      this.fields = { ...this.fields, ...fields }
      this.errors = Object.assign({}, validate(this.fields))
      this.onChange({
        isValid: Object.keys(this.errors).length === 0,
        fields: this.fields
      })  
    }   
  }
}
</script>
```

呼び出し元はこのように書けます。

```vue:pages/vue-form-validation/example2.vue
<template>
  ...
  <UserNameFields :onChange="handleChangeUserNameFileds" />
  ...
</template>

<script>
import UserNameFields from '~/components/forms/user-name-fields'

export default {
  components: {
    UserNameFields,
  }
  data () {
    return {
      isUserNameFieldsValid: false,
      fields: {}
    }
  },
  methods: {
    handleChangeUserNameFileds ({isValid, fields}) {
      this.fields = { ...this.fields, ...fields }
      this.isUserNameFieldsValid = isValid
    },
    ...
  }
}
</script>
```

このようにする事によって Page Component の記述量がかなり軽減されます。今回の案件では問合せ画面がかなり多いらしいので、Page Component を大量に作成する事になりそうです。そうなると同じ処理を繰り返さないように Form Component に Validation を任せるのも一案だと考えています。

## 懸念点

すべての Validation の処理が同期で行われるのであればこれでも良いかと思いましたが、郵便番号を入力した際に、住所を補完するために非同期で API を問合せする処理も必要になると思います。

場面によって一長一短があるのでどこで関心を分離するのかはまだ私の中で決着が着いていません。

## まとめ

最後に Validation の処理を Page Component ではなく、子となる component に任せる方法について言及しました。たぶん似たような Form 画面を大量に作成する案件と出会う事はほとんどないと思うので基本的に前回、前々回に書いた記事の内容で事足りると思いますのでどちらの Component がどこまで関心を持つべきなのかはまだ私の中で決着はついていません。

長々と書き綴ってきましたが、これはいろいろあるやり方の一つでしかないので各自で適宜判断してもらえればと思います。そしてきっと後世のエンジニアたちがもっと素晴らしい手法を発見して記事にしてくれると思いますので私からは以上とさせて頂きます。