Vue.jsによるバリデーションのサンプルを作ってみた(２)

[前回実装した](https://qiita.com/ikdysk/items/0a16f29930afe53669b2)リアルタイムのバリデーションがいまいち意図した動作にならなかったので作り直してみました。

<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="js,result" data-user="YusukeIkeda" data-slug-hash="OJVqPjo" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="OJVqPjo">
  <span>See the Pen <a href="https://codepen.io/YusukeIkeda/pen/OJVqPjo">
  OJVqPjo</a> by YusukeIkeda (<a href="https://codepen.io/YusukeIkeda">@YusukeIkeda</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

##watchを使えばよかったみたい

結論から書くと、リアルタイムで入力値を検証するようなフォームを実装する場合は```computed```ではなく```watch```を使うと良いようです。
```computed```を使用して実装しようとすると、初回アクセス時の空白の時の入力値が空なのも監視されてしまいますが、```watch```ではうまく動きました。

##HTML・CSS

HTML・CSSについては前回と大きな変更はありません。

```html:HTML・CSS

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Vue.jsによるサンプルバリデーション</title>
<style>
[v-cloak] {
  display: none;
}
table td{
  width: 200px;
}
table input[type="text"]{
  width: 100%;
}
</style>
</head>
<body>
<div id="app">
  <div v-cloak>
    <p v-show="checkName.valid">{{checkName.error}}</p>
    <p v-show="checkTel.valid">{{checkTel.error}}</p>
    <p v-show="checkEmail.valid">{{checkEmail.error}}</p>
  </div>
  <table>
    <tr>
        <th>名前：</th>
        <td><input type="text" v-model="checkName.message" value=""></td>
    </tr>
    <tr>
        <th>電話番号：</th>
        <td><input type="text" v-model="checkTel.message" value=""></td>
    </tr>
    <tr>
        <th>メール：</th>
        <td><input type="text" v-model="checkEmail.message" value=""></td>
    </tr>
    </table>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.6/dist/vue.js"></script>
<script src="script.js"></script>
</body>
</html>
```

##JavaScript

JavaScriptについては先程記述したとおり、Vue.jsのバリデーションは```computed```ではなく```watch```で値を監視するようにしました。それによって、初回アクセス時に入力が空の時にバリデーションに引っかかる事がなくなりました。ちなみに、初回アクセス時にバリデーションを検知させたい場合は```immediate:true```を指定する必要があります。
また、```deep:true```はオブジェクトの内側のネストされたプロパティの変更を監視したい時に設定するもので、```handler```はウォッチャがオブジェクト内の変更を感知した際に実行する処理を記載する場所となります。

```javascript:script.js
let validation = (val, type, max) => {
    let checkVal = val.message.length;
    if(checkVal == 0) {
        val.valid = true;
        val.error = `${type}は必須です`;
    } else if(checkVal > max) {
        val.valid = true;
        val.error = `${type}は${max}文字以内で入力してください`;
    } else {
        val.error = "";
        val.valid = false;
    }
}

let vm = new Vue({
  el: '#app',
  data: {
    checkName: {
        message: '',
        error: '',
        valid: false,
    },
    checkTel: {
        message: '',
        error: '',
        valid: false,
    },
    checkEmail: {
        message: '',
        error: '',
        valid: false,
    },
  },
  watch: {
    checkName: {
      handler(val) {
        validation(val, '名前', 10);
      },
      deep: true
    },
    checkTel: {
      handler(val) {
        validation(val, '電話番号', 10);
      },
      deep: true
    },
    checkEmail: {
      handler(val) {
        validation(val, 'メールアドレス', 10);
      },
      deep: true
    },
  }
});
```

#課題とまとめ

実際に運用されているサイトに組み込むには、以下のようなケースも考えて実装する必要がありそうなのでもう少し改良が必要ですが最低限のたたき台はできました。

```text:追加で考える仕様

・すべてのバリデーションを通過したら送信ボタンをアクティブにするような処理。
・正規表現によるバリデーションの実装。
・日付、年齢によるバリデーションの実装。
・電話番号の入力フォームが分割されていた場合の処理。
```

上に挙げた仕様は頻出するので導入するのであれば事前に実装の方針を考えておいたほうが良さそうです。