Nuxt.jsとVeeValidate3でお手軽バリデーション

[diffeasy Advent Calendar 2019](https://qiita.com/advent-calendar/2019/diffeasy) の1日目の記事です。

みなさん、アプリケーションのバリデーションチェック、どうしてますか？
僕は一回、[「Vuelidate使ってみた」の記事](https://qiita.com/tk07Sky/items/21dc9205ccb750bffbbf)を書いた時に

**「今後はVuelidate使ってみたい」**

って書いたんですけど、やっぱり辛くて結局それ以降使ってなかったです😇
（バリデーションをかけるフォームが増えれば増えるほど、エラーのためのcomputedが生え続けるので…）

で今回は、久しぶりにチェックしたらv3に進化してたVeeValidateを使って、お手軽バリデーションを作っていきたいと思います💪


## 環境

Nuxt.js 
VeeValidate v3.1.1


## Install

```bash:shell
# STEP1: 新規Nuxtプロジェクト生成（既存プロジェクトに導入する場合は飛ばしてOK）
$ yarn create nuxt-app vee-validate-sample && cd $_

# STEP2: VeeValidateのインストールとプラグイン作成
$ yarn add vee-validate
$ vim ./plugins/vee-validate.js
```

```js:plugins/vee-validate.js
import Vue from 'vue';
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate';
import ja from 'vee-validate/dist/locale/ja.json';

// VeeValidate2と違って、公式が提供しているバリデーションルールは自分で入れる必要があるので、全ルールをインポートする
// (公式のルールを使わない場合は記述しなくて良い）
// 公式のルールはこちら
// https://logaretm.github.io/vee-validate/guide/rules.html#rules
import * as originalRules from 'vee-validate/dist/rules';
for (let rule in originalRules) {
  extend(rule, {
    ...originalRules[rule],
    message: ja.messages[rule],
  });
}

// バリデーションメッセージの日本語化
localize({
  ja: { messages: { ...ja.messages } },
});
localize('ja');

Vue.component('ValidationProvider', ValidationProvider);
Vue.component('ValidationObserver', ValidationObserver);
```
↑をコピペして貼り付けたら `ESC → wq → Enter`

```bash:shell
$ vim nuxt.config.js
```

```js:nuxt.config.js
export default {
  // ↑で書いたプラグインを選択
  plugins: ['@/plugins/vee-validate.js'],
  ...
  build: {
    // Nuxt.jsではbuildのtranspileにこの設定が必要になる。
    // 詳しく知りたい人はこちら
    // https://logaretm.github.io/vee-validate/guide/rules.html#importing-rules-in-nuxt-js
    transpile: ['vee-validate/dist/rules'],
    ...
  },
};
```

↑をコピペして貼り付けたら `ESC → wq → Enter`


## Using

VeeValidate3を使う上で、大きく2つのコンポーネントが存在します。
それは`ValidationProvider`と`ValidationObserver`です。


### ValidationProvider
`ValidationProvider`で入力項目を囲む（一つだけ）と、中にある入力項目に対してバリデーションをかけることができます。
`ValidationProvider`の中には、1つ以上の`v-model`を持つ入力フォーム(`input`など)が必要です。
主な機能は下の感じです。

- rulesオプションにバリデーションルールを記述する
  - ルールを記述する際は`String`、もしくは`v-bind:rules`に`Object`で書く
  - ex. String→`rules="required"` Object→`:rules="{ required: true }"`
- `v-slot`でエラー状態を配列で受け取ったり、バリデーションの状態を受け取ることができる

**使用例**

```vue:index.vue
<template>
  <ValidationProvider v-slot="{ errors }" rules="required|email">
    <input type="email" v-model="email" />
    <span>{{ errors[0] }}</span>
  </ValidationProvider>
</template>

<script>
export default {
  data() {
    return {
      email: '',
    };
  },
};
</script>
```


### ValidationObserver

`ValidationObserver`を使うと、バリデーショングループを作ることができます。
バリデーショングループを作ると、`ValidationProvider`の状態（今バリデーションに通っているか通っていないか、など）を見たりすることができます。
`ValidationObserver`の中には、1つ以上の`ValidationProvider`が必要です。
主な機能は下の感じです。

- `v-slot`で`handleSubmit(methods)`を受け取れば、バリデーションルールに通った後、アクションを実行することができる
- `v-slot`では他に、valid時、invalid時などの状態も取得でき、バリデーションに引っかかっていたらボタンを非活性にする、などが簡単にできる

**使用例**

```vue:index.vue
<template>
  <ValidationObserver v-slot="{ handleSubmit, invalid }">
    <form @submit.prevent="handleSubmit(login)">
      <!-- email -->
      <ValidationProvider v-slot="{ errors }" rules="required|email">
        <label>
          メールアドレス:
          <input type="email" v-model="email" />
        </label>
      </ValidationProvider>
      <!-- password -->
      <ValidationProvider v-slot="{ errors }" rules="required">
        <label>
          パスワード:
          <input type="password" v-model="password" />
        </label>
      </ValidationProvider>
      <button type="submit" :disabled="invalid">ログイン</button>
    </form>
  </ValidationObserver>
</template>

<script>
export default {
  data() {
    return {
      email: '',
      password: ''
    };
  },
  methods: {
    login() {
      // メールアドレスとパスワードのバリデーションルールに通らないと、このメソッドは実行されない
    },
  },
};
</script>
```

`ValidationObserver`タグや、`ValidationProvider`タグが増えてテンプレートが少し大きくはなりますが、どのフォームにバリデーションをかけているのか明確になるので僕は好きだなぁと思っています✌️


## 自作バリデーションを作る

VeeValidateを使うと、簡単にバリデーションを作ることができます。
「パスワードは何文字以上で〜」とかサービスによって変化すると思いますが、もしそうであってもサクッと自作できるので安心してください。

例えば、ひらがなのみ入力するフォーム用のバリデーションを作ってみましょう。
下のように、バリデーションルールを作ってVeeValidateの`extend`メソッドで登録してあげればいいです。

```js:plugins/vee-validate.js
import Vue from 'vue';
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate';
import ja from 'vee-validate/dist/locale/ja.json';

import * as originalRules from 'vee-validate/dist/rules';
for (let rule in originalRules) {
  extend(rule, {
    ...originalRules[rule],
    message: ja.messages[rule],
  });
}

// ひらがなバリデーションを追加する
const hiragana = /^[\u3040-\u309f\u0020\u00a0\u3000\u30fb\u30fc]+$/;
const isHiragana = {
  message: 'This field input in Hiragana.',
  validate(value) {
    return hiragana.test(value);
  },
};
extend('isHiragana', isHiragana);

// バリデーションメッセージの日本語化
localize({
  ja: { messages: { ...ja.messages } },
});
localize('ja');

Vue.component('ValidationProvider', ValidationProvider);
Vue.component('ValidationObserver', ValidationObserver);
```

これで、`ValidationProvider`の`rules`に追加してあげれば、ひらがな以外が入力された際にバリデーションエラーが発火すると思います。

```vue:index.vue
<template>
  <!-- 必須入力とひらがなバリデーションを追加 -->
  <ValidationProvider v-slot="{ errors }" rules="required|isHiragana">
    <input type="text" v-model="hiragana" />
    <span>{{ errors[0] }}</span>
  </ValidationProvider>
</template>
```


## バリデーションメッセージを独自で編集

バリデーションメッセージは、時に独自でつけたい時があるかと思います。
そんなときも、VeeValidateならサクサク登録できます。

例えば、さっき登録したひらがなバリデーションルール用にメッセージを追加してみます。
下のように、バリデーションメッセージを任意の名前をつけて作って登録し、`ValidationProvider`の`name`にその名前を登録すると、バリデーションメッセージが独自のものに切り替わると思います。

```js:plugins/vee-validate.js
import Vue from 'vue';
import { ValidationObserver, ValidationProvider, extend } from 'vee-validate';
import ja from 'vee-validate/dist/locale/ja.json';

import * as originalRules from 'vee-validate/dist/rules';
for (let rule in originalRules) {
  extend(rule, {
    ...originalRules[rule],
    message: ja.messages[rule],
  });
}

const hiragana = /^[\u3040-\u309f\u0020\u00a0\u3000\u30fb\u30fc]+$/;
const isHiragana = {
  message: 'This field input in Hiragana.',
  validate(value) {
    return hiragana.test(value);
  },
};
extend('isHiragana', isHiragana);

// 独自メッセージを作って登録
const originalValidationMessage = {
  fields: {
    furigana: {
      required: 'この欄は必須入力です',
      isHiragana: 'この欄はひらがなで入力してください',
    },
  },
};
localize({
  ja: { ...originalValidationMessage, messages: { ...ja.messages } },
});
localize('ja');

Vue.component('ValidationProvider', ValidationProvider);
Vue.component('ValidationObserver', ValidationObserver);
```

```vue:index.vue
<template>
  <!-- nameに"furigana"を設定する -->
  <ValidationProvider v-slot="{ errors }" rules="required|isHiragana" name="furigana">
    <input type="text" v-model="hiragana" />
    <!-- ここに表示されるメッセージが、"furigana"で設定したメッセージに切り替わる -->
    <span>{{ errors[0] }}</span>
  </ValidationProvider>
</template>
```

## おわりに

一通り読んでいただいて、簡単にバリデーションを追加できると思いませんか？
アプリケーションを爆速で作る上で、バリデーションどうするか問題って結構あると思っていますし、全部自分で書くってなったら結構辛いと思います。
もし「良さそうだな〜」って思っていただけたら、ぜひ導入を検討してみてください👋