始めに
フォームのバリデーションで、他のフィールド値と関連したバリデーションをしたいことがあると思います。
例
+ 最低値から最大値の範囲
+ 開始日から終了日の範囲
vee-validateでは@名前
でValidationProviderの名前を探してその値を入れて渡すことができるため、それでチェックすることが可能です。
ValidationObserver
//- 他のValidationProviderで設定したnameから値を参照する
ValidationProvider(
rules="max_value:@max"
v-slot="{ errors }"
)
input(
v-model="$data.minValue"
type="number"
)
.error {{ errors[0] }}
//- nameを設定しておく
ValidationProvider(
name="max"
rules=""
)
input(
v-model="$data.maxValue"
type="number"
)
これでバリデーションはできるようになりましたが、エラー文言をもう一方の入力フィールドに伝えることができません。なので、エラー文言の表示のやり方を工夫する必要があります。
ここではエラーの表示方法についていくつか案を出したので参考にしてもらえれば幸いです。
バリデーション結果をdataに持って表示する方法
一番単純なのは手動でバリデーションして、その結果をdataに持つやり方です。vee-validateの自動でバリデーションするメリットを潰してしまいますが、これが一番愚直な方法だと思います。
<template lang="pug">
ValidationObserver
div
ValidationProvider(
ref="provMin"
rules="min_value:0|max_value:@max"
v-slot="{ errors, invalid }"
)
input(
v-model="$data.min"
:class="{ '-error': invalid }"
type="number"
)
ValidationProvider(
name="max"
ref="provMax"
rules="max_value:10"
v-slot="{ errors, invalid }"
)
//- 両方のエラーのスタイルを当てる場合はref経由でエラーのルールを調べる必要がある(あるいはdataで持っておくか)
input(
v-model="$data.max"
:class="{ '-error': invalid || ($refs.provMin && !!$refs.provMin.failedRules.max_value) }"
type="number"
)
.error {{ $data.errorMessage }}
</template>
<script>
export default {
data() {
return {
min: 0,
max: 10,
};
},
created() {
this.$watch(
() => [this.$data.min, this.$data.max],
async () => {
await this.$nextTick();
// バリデーションしてから結果を代入する
await this.$refs.provMin.validate();
await this.$refs.provMax.validate();
this.$data.errorMessage = this.$refs.provMin.errors[0] || this.$refs.provMax.errors[0];
}
);
},
};
</script>
PortalVueで表示場所を移動する
DOMの場所は変えませんが、PortalVueを使って最終的に表示位置を移動する方法です。手動でバリデートを実行する必要は無くなりましたが、エラー文言を1つだけにする場合はPortalを使うタイミングを調整する必要があるので少し手間です。
<template lang="pug">
ValidationObserver
div
ValidationProvider(
ref="provMin"
rules="min_value:0|max_value:@max"
v-slot="{ errors, invalid }"
)
input(
v-model="$data.min"
:class="{ '-error': invalid }"
type="number"
)
//- エラーの時だけportalを通す
template(v-if="invalid")
Portal(to="error")
div {{ errors[0] }}
ValidationProvider(
name="max"
ref="provMax"
rules="max_value:10"
v-slot="{ errors, invalid }"
)
input(
v-model="$data.max"
:class="{ '-error': invalid || ($refs.provMin && !!$refs.provMin.failedRules.max_value) }"
type="number"
)
//- 前のバリデーションが通っている時だけportalを通す
template(v-if="invalid && ($refs.provMin && $refs.provMin.flags.valid)")
Portal(to="error")
div {{ errors[0] }}
.error
PortalTarget(name="error")
</template>
一つにまとめたコンポーネントを使う
今まで見たやり方はrefを使ったり少しトリッキーなことをしたりするので扱いづらかったと思います。なので今度はもうシンプルに入力範囲フォームという一つのコンポーネントを作ってしまい、それをバリデーションするという方法です。
凄くシンプルになりましたが、この場合は片方だけバリデーションするということができなくなります。
<template lang="pug">
ValidationObserver
ValidationProvider(
rules="range_value:0,10"
v-slot="{ errors }"
)
//- 一つのコンポーネントにまとめる
InputRange(
v-model="$data.range"
:errorMessage="errors[0]"
)
</template>
終わりに
以上が他のフィールド値も含めてバリデーションする方法でした。どれも一長一短があると思いますが、最後に説明した一つにまとめたコンポーネントを使うのがスッキリしているのかなと思いました。
最後にサンプルコードを共有しますので、興味がある方は是非見てください。