KJ DEV
How to

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

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

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

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

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

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

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

目次
  1. ネストしたコンポーネントにバリデーション
    1. カスタムコンポーネントをv-modelに対応させる
    2. propsにvalueを指定してVeeValidateに入力値を反映させる
      1. this.value以外のプロパティをVeeValidateでバリデーション
  2. フォーム要素にVeeValidateを適用する
    1. radioだけはバリデーションに工夫が必要
  3. フォーム要素のバリデーションのサンプル

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

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をスムーズに使えるのではないかと思います。

開発で参考になった本

実際に読んでみて開発に役立った本を紹介しています。

これから本格的にデザインシステムを学んで作りたい時にとても参考になる一冊でした。デザインシステムついて幅広く触れられているけど、「tailwindを触ってデザインシステムに興味を持った」という人でも少しずつ取り入れやすいです。