😀

VeeValidateでネストしたコンポーネントをバリデーションする方法

2020/04/06に公開

Vue.jsでバリデーションかけるならVeeValidate。

簡単に使えて高機能でいいのですが、コンポーネントをネストして使おうとするとなんだか難易度がめちゃくちゃ上がってない?と思ってしまうライブラリ。

チェックボックスやラジオボタンってCSSでオリジナルのものを使うために別途コンポーネント化するってかなりある話だと思います。

でもコンポーネント化しちゃったらVeeValidateとどうやって連携させるんだっけ?と調べたり考えたりしてしまいます。

というわけでネストしたコンポーネントにVeeValidateを適用する方法を書いていきたいと思います。

vee-validateの最新バージョンは3系ですが、まだまだ利用の多い2系を使います。

ネストしたコンポーネントにバリデーション

VeeValidateをネストしたコンポーネントに適用するためにはv-modelを受け付けられるようにする必要があります。

inputのtype="text"のカスタムコンポーネントを作る時の例を見ていきます。

下記がVeeValidateを対応されられるようなコンポーネントの基本的なコードです。

<template>
  <div class="field" :class="{ 'is-error': isError }">
    <input :name="name" type="text" class="field__input" :value="value" @input="onInput">
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: String
    },
    name: {
      type: String,
      default: ""
    },
    isError: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    onInput(e) {
      this.$emit("input", e.target.value);
    }
  }
};
</script>

このコードで重要な点が2点あります。

カスタムコンポーネントをv-modelに対応させる

v-modelに対応させるにはv-bind:inputで変更時にthis.$emitでinputイベントを発行する必要があります。

<template>
  <div>
    <input type="text" @input="onInput" />
  </div>
</template>

<script>
export default {
  methods: {
    onInput(e) {
      this.$emit('input', e.target.value)
    }
  }
}
</script>

値が変更されたときにthis.$emitを使ってinputイベントを発行することでコンポーネントをv-modelに対応可能です。

propsにvalueを指定してVeeValidateに入力値を反映させる

VeeValidateはバリデーションする値はデフォルトでthis.valueが適用されます。

propsにvalueを設定するとVue内部で保持している値にアクセスできます。
Vueはattrsというインスタントプロパティを保持していて、その中のattrs.valueへのアクセスをpropsに設定することでthis.valueで値を取得可能になります。

this.value以外のプロパティをVeeValidateでバリデーション

先に書いたようにVeeValidateはデフォルトではthis.valueの値でバリデーションを実行しますが、任意のプロパティでバリデーションをかけることもできます。

export default {
  $_veeValidate: {
    value() {
      return this.customProperty
    }
  }
}

このように**$_veeValidateにvalueという関数を定義して任意の値をreturn**することで、その値を使ってバリデーションをかけることができます。

$_veeValidateについては公式サイトの下記で記載があります。

http://vee-validate.logaretm.com/v2/concepts/components.html#how-it-works

フォーム要素にVeeValidateを適用する

checkbox、radio、selectなどフォームの要素はテキストフィールド以外にもありますが、基本的には紹介してきたコードをほとんど同じで適用できます。
radioだけは少し違うので後述します。

例えばチェックボックスのコードです。

<template>
  <div class="checkbox" :class="{ 'is-error': isError }">
    <input :name="name" type="checkbox" :checked="checked" @input="onInput">
  </div>
</template>

<script>
export default {
  props: {
    value: {
      type: Boolean
    },
    checked: {
      type: Boolean,
      default: false
    },
    name: {
      type: String,
      default: ""
    },
    isError: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    onInput(e) {
      this.$emit("input", e.target.checked);
    }
  }
};
</script>

テキストフィールドと比較するとチェックしているかどうかの初期値を受け付けるpropsがあるかどうかだけです。

valueをpropsで定義して、値の変更時にthis.$emitでinputイベントを発行しています。

radioだけはバリデーションに工夫が必要

radioは1つのname属性に対していくつかの選択肢を用意することが多いと思います。
どのradioがチェックされたのか取得するためにvalueを受け付けられるようにする必要があります。

propsをvalueにしてしまうと先で触れた$attrs.valueと競合しまうので、radioValueというpropsを受け付けられるようにします。

<template>
  <div class="radio" :class="{ 'is-error': isError }">
    <input :name="name" :value="radioValue" type="radio" :checked="checked" @input="onInput">
  </div>
</template>

<script>
export default {
  $_veeValidate: {
    value() {
      return this.isChecked
    }
  },
  props: {
    radioValue: {
      type: String,
      default: ''
    },
    checked: {
      type: Boolean,
      default: false
    },
    name: {
      type: String,
      default: ""
    },
    isError: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      isChecked: this.checked
    }
  },
  methods: {
    onInput(e) {
      this.isChecked = e.target.checked
      this.$emit("input", e.target.value);
    }
  }
};
</script>

後は特定のradioがチェックされているのか、isCheckedで保持してVeeValidateに適用されるように$_veeValidateを設定します。

フォーム要素のバリデーションのサンプル

今回はテキストフィールドとチェックボックスのコードを紹介してきましたが、テキストフィールド、チェックボックス、ラジオボタン、セレクトを含めたバリデーションするコードをCodeSandboxで書いてみました。

https://codesandbox.io/embed/veevalidate2-nested-component-mdgee?fontsize=14&hidenavigation=1&theme=dark

VeeValidateはとても便利ですが各フォーム要素をコンポーネント化すると使う難易度が結構上がっちゃうよなあ・・と思っています。

ですがコンポーネントを分割したときの基本的な対応を理解しておけば、色んな仕様にも対応させることができます。

今回紹介した

  • v-modelに対応させる
  • propsにvalueを指定する

の2点を心がけておくとVeeValidateをスムーズに使えるのではないかと思います。

Discussion

ログインするとコメントできます