VeeValidateでネストしたコンポーネントをバリデーションする方法
Vue.jsでバリデーションかけるならVeeValidate。
簡単に使えて高機能でいいのですが、コンポーネントをネストして使おうとするとなんだか難易度がめちゃくちゃ上がってない?と思ってしまうライブラリ。
チェックボックスやラジオボタンってCSSでオリジナルのものを使うために別途コンポーネント化するってかなりある話だと思います。
でもコンポーネント化しちゃったらVeeValidateとどうやって連携させるんだっけ?と調べたり考えたりしてしまいます。
というわけで**ネストしたコンポーネントにVeeValidateを適用する方法**を書いていきたいと思います。
vee-validateの最新バージョンは3系ですが、まだまだ利用の多い2系を使います。
- 目次
ネストしたコンポーネントにバリデーション
VeeValidateをネストしたコンポーネントに適用するためには**v-modelを受け付けられるようにする必要があります。**
inputのtype="text"のカスタムコンポーネントを作る時の例を見ていきます。
下記がVeeValidateを対応されられるようなコンポーネントの基本的なコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
<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イベントを発行**する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
<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の値でバリデーションを実行しますが、任意のプロパティでバリデーションをかけることもできます。
1 2 3 4 5 6 7
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だけは少し違うので後述します。
例えばチェックボックスのコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
<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を受け付けられるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
<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をスムーズに使えるのではないかと思います。