Vue.jsを使ってて、フロントサイドのバリデーションには VeeValidate が便利

先日、[フロントサイドのバリデーションにはvalidatorjsが便利](http://qiita.com/hirohero/items/e51ff5a98a0eea425d44)という記事を拝見し、現在作っているシステムで使ってみようとしたのですが、挫折しました。

理由としては、複雑なフロントを作ろうとしているためで、下記の要件を満たす必要がありました。

- 1:n のデータで、ユーザーが追加できる要素がある (例: 複数メールアドレス登録)
- ページローティング直後はバリデーションしない
    - ユーザーがインプット開始時に、 **入力中の要素のみ** バリデーション
- フォームを送信する直前に **全ての要素** に対してバリデーション
    - エラーのある要素は赤くする
- エラーのある **要素の直後** に、 「メールアドレスは必須項目です。」などというメッセージを表示
- バリデーションのルールは一箇所にまとめたい。入力中と送信時でほぼ使いまわしたい。

そこで見つけたのが `VeeValidate` です。

https://github.com/logaretm/vee-validate

素晴らしかったので使い方を書いておきます。



# 単純なバリデーション

Emailを送信するフォームがあって、下記のタイミングでバリデーションが働くようにします。

- 入力時
- 送信時

![out.gif](https://qiita-image-store.s3.amazonaws.com/0/103885/82eaf966-31b9-2bf5-a2d0-e61d86406074.gif)


Promiseでできるのが気持ちいいですね。またバリデーションのルールを `<input>` に置けるのが個人的には好きです。

```jinja:VeeValidationSimple.vue
<template>
  <div>
    <input
       name="email"
       v-model="email"
       v-validate="'required|email'"
       :class="{'has-error': errors.has('email')}">
    <p v-if="errors.has('email')" class="alert-danger">
      {{ errors.first('email') }}
    </p>

    <button @click="register">Register</button>
  </div>
</template>

<script>
import Vue from 'vue'
import VeeValidate from 'vee-validate'

Vue.use(VeeValidate)

export default {
  data () {
    return {
      email: ''
    }
  },
  methods: {
    register () {
      this.$validator.validateAll().then((result) => {
        if (!result) {
          alert(this.errors.all())
          return
        }
        alert('Hello, ' + this.email)
      })
    }
  }
}
</script>

<style>
.alert-danger {
  color: red;
}
.has-error {
  border-color: red;
}
</style>
```


# 複数の要素に対するバリデーション

意外と簡単でした。
`name`で識別しているらしいので、関連する属性値に連番を振ってあげればOKです。

```jinja:VeeValidationMultiple.vue
<template>
  <div class="hello">

    <!-- 要素に連番を振る -->
    <div v-for="(account, i) in accounts">
      <input
         :name="'email' + i"
         v-model="account.email"
         v-validate="'required|email'"
         :class="{'has-error': errors.has('email' + i)}">
      <p v-if="errors.has('email' + i)" class="alert-danger">
        {{ errors.first('email' + i) }}
      </p>
    </div>

    <p>
      <button @click="addAccount">Add Account</button>
      <button @click="register">Register</button>
    </p>
  </div>
</template>

<script>
import Vue from 'vue'
import VeeValidate from 'vee-validate'

Vue.use(VeeValidate)

export default {
  data () {
    return {
      // v-forで回す要素は、オブジェクトの配列にする
      accounts: [{
        email: ''
      }]
    }
  },
  methods: {

    // 追加
    addAccount () {
      this.accounts.push({
        email: ''
      })
    },

    register () {
      this.$validator.validateAll().then((result) => {
        if (!result) {
          alert(this.errors.all())
          return
        }
        alert('Hello, ' + this.accounts)
      })
    }

  }
}
</script>

<style>
.alert-danger {
  color: red;
}
.has-error {
  border-color: red;
}
</style>
```

こんな感じになります。

![out.gif](https://qiita-image-store.s3.amazonaws.com/0/103885/64ed17cc-96bc-29ca-59f1-1322a19a12f8.gif)

ちゃんと、後から追加された要素に対してもバリデーションが効いています。

# 参考

- https://m.dotdev.co/form-validation-using-vue-js-2-35abd6b18c5d
- http://vee-validate.logaretm.com/index.html

ドキュメントも豊富です。

今回のソースコードも貼っておきます。
https://github.com/acro5piano/vue-cli-test/blob/master/src/components/VeeValidation.vue