Nuxt.jsでバリデーションするためにVeeValidateを使う

公開日:2019/09/14 更新日:2019/09/14
Nuxt.jsでバリデーションするためにVeeValidateを使うのサムネイル

はじめに

Nuxt.jsでフォームのバリデーションをするためにVeeValidateを使ったので設定手順と簡単な使い方をまとめます。なお、VeeValidateはバージョン2系と最新の3系では大きく変わっています。この記事では、最新のバージョン3系についてまとめます。 

前提と環境

Nuxt.jsとVeeValidateのバージョンは以下の通りとします。

  • Nuxt.js : 2.9.2
  • vee-validate: 3.0.5

VeeValidateの公式ドキュメントは以下です。

logaretm.github.io

Template Based Validation Framework for Vue.js

VeeValidateをインストールする

npmでインストールします。

$ npm install vee-validate --save

VeeValidate用のプラグインファイルを作成する

VeeValidateの設定をまとめておくプラグインファイルを作成します。Nuxt.jsアプリのルートディレクトリにpluginsディレクトリがあるので、そこにvee-validate.jsというファイル名で以下の内容を記述します。ValidationProviderValidationObserverの使い方は後述します。

plugins/vee-validate.js
import Vue from 'vue'
import { ValidationProvider, ValidationObserver, localize, extend } from 'vee-validate' // 使用する機能
import ja from 'vee-validate/dist/locale/ja.json' // エラーメッセージの日本語化用
import { required, max, email } from 'vee-validate/dist/rules' // 使用するバリデーションルール

// VeeValidateがデフォルトで用意している各ルールを使用するよう指定
extend('required', required) // 必須項目のバリデーション
extend('email', email) // emailのバリデーション
extend('max', max) // 最大文字数のバリデーション

Vue.component('ValidationProvider', ValidationProvider)
Vue.component('ValidationObserver', ValidationObserver)
localize('ja', ja)

もし、VeeValidateが用意している全てのルールを使用したい場合は、以下のようにすることで全てのルールをインポートすることができます。

plugins/vee-validate.js
import Vue from 'vue'
import { ValidationProvider, ValidationObserver, localize, extend } from 'vee-validate' // 使用する機能
import ja from 'vee-validate/dist/locale/ja.json' // エラーメッセージの日本語化用
import * as rules from 'vee-validate/dist/rules' // 全てのバリデーションルール

// forループで全てのバリデーションルールをextendで登録する
for (let rule in rules) {
  extend(rule, rules[rule])
}

Vue.component('ValidationProvider', ValidationProvider)
Vue.component('ValidationObserver', ValidationObserver)
localize('ja', ja)

VeeValidateで使用できるルール一覧は、以下の公式ドキュメントに全て記載されています。十分種類が多いです。

logaretm.github.io

VeeValidate offers common validators that will cover most apps needs:

nuxt.config.jsに設定を追記する

作成したvee-validate.jsを読み込むために以下の記述をnuxt.config.jsに加えます。

nuxt.config.js
module.exports = {
  mode: 'universal',
// ...省略...
/*
  ** Plugins to load before mounting the App
  */
  plugins: [
     // 以下を追記
    { src: '@/plugins/vee-validate'}
  ],

// ...省略...

 build: {
    // 以下を追記
    transpile: [
      "vee-validate/dist/rules"
    ],
    /*
     ** You can extend webpack config here
     */
    extend(config, ctx) {}
  }

上記のpluginsにて、作成したvee-validate.jsを指定しています。また、build部分には、transplieという項目を追加していますが、これは各自のNuxt.jsの設定によって必要になる場合があります。もしUmexpected token exportというエラーが自身のNuxt.jsで表示される場合は、この記述を追記してみてください。

VeeValidateを使ってバリデーションを行う

VeeValidateでは、ValidationProviderというコンポーネントを使って任意のフォームにバリデーションを実装することができます。例えば、以下は適当な例となりますが、フォームの項目を入力必須としたい場合には以下のようになります。

pages/form.vue
<template>
  <section class="section">
    <div class="columns is-mobile">
      <div class="field">
        <label class="label">テキストフォーム</label>
          <div class="control">
            <validation-provider v-slot="{ errors }" rules="required" name="テキスト項目">
             <input v-model="textValue" class="input" type="text" placeholder="Text input" />
             <p v-show="errors.length" class="help is-danger">
               {{ errors[0] }}
             </p>
           </validation-provider>
        </div>
      </div>
    </div>
  </section>
</template>

<script>
import Card from '~/components/Card'

export default {
  name: 'HomePage',
  data () {
    return {
      textValue: ''
    }
  }
}
</script>

上記の例では、テキストフィールドに入力内容がない場合は以下のように必須項目である旨のメッセージが表示されます。

text-required-demo.png

Nuxt.jsでVeeValidateを使用する際の注意点として、ValidationProviderをコンポーネントととして使う場合、ケバブケース(ハイフン区切り)で使います。すなわち、となります。ただ、これは各自の設定によります。VeeValidateの公式ドキュメントでは、というようにキャメルケースになっているのでそのまま貼り付けてもエラーとなる場合があります。

なお、上記の例では、フォームの送信ボタンに相当するものがなく実用的でないため、バリデーションが通らないと送信ボタンを押せないようにするための例を以降に載せます。

全てのバリデーションが通るまで送信ボタンを無効化する例

実際のフォームでは、複数の入力項目が存在し、各項目に対してバリデーションをかけたい場合が多いと思います。ここでは、ValidationObserverを使って複数項目に対するバリデーションチェックを行い、さらに全てのバリデーションが通るまでは送信ボタンを無効化する例を載せます。前述したform.vueを以下の様にします。

pages/form.vue
<template>
  <section class="section">
    <div class="columns is-mobile">
      <validation-observer v-slot="{ invalid }">
      <div class="field">
        <label class="label">お名前</label>
          <div class="control">
            <text-field-with-validation v-model="textValue" rules="required" fieldname="お名前" />
        </div>
      </div>
      <div class="field">
        <label class="label">メールアドレス</label>
          <div class="control">
            <text-field-with-validation v-model="emailValue" rules="required|email" fieldname="メールアドレス" />
        </div>
      </div>
      <div class="field">
          <div class="control">
            <button class="button is-link" :disabled="invalid">送信する</button>
            <p v-show="invalid" class="help is-danger">
             全ての必須項目を入力すると「新規追加」ボタンが有効化されます。
            </p>
        </div>
      </div>
    </validation-observer>
    </div>
  </section>
</template>

<script>
import TextFieldWithValidation from '@/components/TextFieldWithValidation'

export default {
  name: 'HomePage',
  components: {
    TextFieldWithValidation
  },
  data () {
    return {
      textValue: '',
      emailValue: ''
    }
  }
}
</script>

ポイントとなるのは、の間にあるフォームはVeeValidateの監視下となり、その中にあるフォームでバリデーションが通らないものがある場合はinvalidがtrueとなります。そしてこのinvalidがtrueである場合は、送信ボタンに:disabled="invalid"を付与することで無効化しています。

また、メールアドレスのフォームでは、rules="required|email"としており、これはrequired(必須項目のバリデーション)、email(正しいメールアドレスのバリデーション)の2つを指定しています。このようにパイプ|でルールを繋ぐことで複数指定することができます。

なお、上記のは、新しく作成したコンポーネントです。各自のNuxt.jsアプリのルートディレクトリにあるcomponentsディレクトリ下に作成し、以下のようにします。

components/TextFieldWithValidation.vue
<template>
  <validation-provider v-slot="{ errors }" :rules="rules" :name="fieldname">
    <input
       v-model="innerValue"
      :placeholder="fieldname"
    />
    <p v-show="errors.length" class="help is-danger">
      {{ errors[0] }}
    </p>
  </validation-provider>
</template>

<script>
export default {
  name: 'TextFieldWithValidation',
  props: {
    rules: {
      type: String,
      required: true
    },
    value: {
      type: String,
      required: true
    },
    fieldname: {
      type: String,
      required: true
    }
  },
  computed: {
    innerValue: {
      get () {
        return this.$props.value
      },
      set (val) {
        this.$emit('input', val)
      }
    }
  }
}
</script>

上記のform.vueは表示すると以下のような見た目になります。

validation-observer-demo.png

具体的な説明は公式ドキュメントに譲りますが、おそらく各記述と実際のフォームの見た目を照らし合わせれば理解できると思います。 なお、上記の例では、バリデーションが通るまではボタンを無効化していますが、もし:disabledを付与せずに試しにバリデーションが通る前に送信してみると、実際には送信できてしまいます。 すなわち、例えば送信ボタンのクリックに何かしらのメソッドを結びつけている場合は、そのメソッドは実行されます。これを防ぐには、以下のようにValidationObserverにpassesを加えて、さらに送信ボタンに結びつけるメソッドをpasses(submit)のように引数としてpassesに与えます。以下は公式ドキュメントからの引用です。

Example from Official Docs
<template>
  <validation-observer v-slot="{ invalid, passes }">
    <form @submit.prevent="passes(submit)">
      <text-field-with-validation rules="required" v-model="first" />

      <text-field-with-validation rules="required" v-model="second" />

      <button :disabled="invalid">Submit</button>
    </form>
  </validation-observer>
</template>

<script>
export default {
  methods: {
    submit() {
      // メソッドには追記修正不要
    }
  }
};
</script>

バリデーションが通らない時に送信ボタンを押下された場合に自身で処理を記述したい場合は、passesを使わずに以下のようにすることもできます。

Example from Official Docs
<template>
  <validation-observer ref="observer" v-slot="{ invalid }" tag="form" @submit.prevent="submit()">
      <text-field-with-validation rules="required" v-model="first" />

      <text-field-with-validation rules="required" v-model="second" />

    <button :disabled="invalid">Submit</button>
  </validation-observer>
</template>

<script>
export default {
  methods: {
    async submit () {
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        // バリデーションが通る前に送信ボタンがクリックされた場合の処理
      }
       // バリデーションが通っている状態で送信ボタンがクリックされた場合の処理
    }
  }
};
</script>

上記についてはいずれも公式ドキュメントに詳しく記載されています。

フォーム送信後にバリデーションをリセットする(#2019/9/15 追記)

上記のValidatioObserverの例だと、フォームを送信直後にフォーム内容をリセットしています。この場合、フォームを送信して空になった時点でバリデーションがかかってしまいバリデーションエラーが表示されます。これを防ぐには、フォーム送信完了後にリセットする必要がありました。以下がコードになります。

Example from Official Docs
<template>
  <validation-observer ref="observer" v-slot="{ invalid }" tag="form" @submit.prevent="submit()">
      <text-field-with-validation rules="required" v-model="first" />

      <text-field-with-validation rules="required" v-model="second" />

    <button :disabled="invalid">Submit</button>
  </validation-observer>
</template>

<script>
export default {
  methods: {
    async submit () {
      const isValid = await this.$refs.observer.validate();
      if (!isValid) {
        // バリデーションが通る前に送信ボタンがクリックされた場合の処理
      }
       // バリデーションが通っている状態で送信ボタンがクリックされた場合の処理
       // フォーム送信などの処理完了後、以下のリセットを呼び出す。
       requestAnimationFrame(() => {
        this.$refs.observer.reset()
      })
    }
  }
};
</script>

ref="observer"でValidatioObserverの名前を指定しており、これに対するリセットをかけていることに注意してください。 なお、リセットに関する公式ドキュメントは以下になります。

logaretm.github.io

Like the validate method, we could also reset our form after submitting the values to the server

まとめ

VeeValidateはバージョン2系から3系で大きく変わっていますが、3系も一度理解できればすんなり使用でき、また柔軟性も向上している印象です。

関連記事

開発アプリ

nanolog.app

毎日の小さな出来事をなんでも記録して、ログとして残すためのライフログアプリです。