viviONグループでは、DLsiteやcomipoなど、二次元コンテンツを世の中に届けるためのサービスを運営しています。
ともに働く仲間を募集していますので、興味のある方はこちらまで。
🦺 概要
現在担当しているPJでVue2 → Vue3へのアップグレードを進めていて(今更)、PJで使用していたvee-validateもv3 → v4へアップグレードする必要がありました。
今回はひとまずOptions APIのままで進めることになったのですが、vee-validate v4へのマイグレーションに関してOptions APIでの情報があまり存在せずかなり苦労したので、同じ轍を踏んでしまわないようまとめてみました。
🦺 環境
Vue:v2.7.14 → v3.1.0
vee-validate:v3.4.15 → v4.13.2
🦺 アップグレード
まずはvee-validateをv4へアップグレードします。
また、v3で使用していたvee-validate/dist/rules
が廃止され別のパッケージにまとめられているので、それも併せてインストールします。
npm i vee-validate @vee-validate/rules
必要な方は@vee-validate/i18n
も併せてインストールしてください
あとはimport元を一括置換すればOK
例)
- import { ValidationProvider, ValidationObserver, extend } from 'vee-validate';
- import { required, size, ext, email, max } from 'vee-validate/dist/rules';
+ import { Field as ValidationField, Form as ValidationForm, defineRule } from 'vee-validate';
+ import { required, size, ext, email, max } from '@vee-validate/rules';
FieldやdefineRuleについては後述します。
🦺 マイグレーション
ここから、具体的な記法のマイグレーションについて記載します。
Field, Form
先ほどでてきたValidationField
, ValidationForm
に関してですが、それぞれv3のValidationProvider
とValidationObserver
に対応していると考えてもらって大丈夫です。
importを修正したら、template内のProvider, ObserverをすべてField, Formに置換します。
また、v4からはFieldのバリデーション発火タイミングをvalidateOnHoge
で制御できます(基本いじらなくても大丈夫なはず)
Fields(Provider)へのrulesの指定方法についてはv3とほとんど変わらないので、基本そのままで問題ないです
defineRule
これはv3でいうextend
に対応しているものですが、ちょっと書き方が変わります。
例)
- extend('max', {
- ...max,
- message: '{_field_}は、{length}文字以下で指定してください。'
- });
+ defineRule('max', (value, params, { field }) => {
+ return max(value, params) || `${field}は、${params}文字以下で指定してください。`
+ });
defineRule
のcallback関数の引数は、それぞれ
- 実際の値
- 設定している閾値の配列※
- なにも設定していない場合は
null
- なにも設定していない場合は
- validateのcontext
-
FieldValidationMetaInfo
型に紐づいています- field
- form
- label
- name
- rule
- value
-
このようになっています。
※params
の中身はルールによって変わることがあるので、適宜必要なものを取り出します。
v-slot
v3のProviderは入力要素の値を自動で監視してくれていましたが、v4のFieldは明示的に入力要素の値を紐づける必要があります。
FieldのmodelValue
に対して、監視したい値をバインドすればOKです。
- <ValidationProvider name="ユーザーネーム" rules="required|max:50" v-slot="{ errors }">
+ <ValidationField :model-value="name" name="ユーザーネーム" rules="required|max:50" v-slot="{ errors }">
<input
type="text"
id="userName"
v-model="name"
placeholder="ユーザーネームを追加"
class="input"
:class="{'input-error' : errors.length}"
/>
<p v-if="errors.length">
{{ errors[0] }}
</p>
- </ValidationProvider>
+ </ValidationField>
公式的にはFieldに存在するfieldスロットを使ってほしいらしいです(「これをbindするだけで必要な情報が全部渡せるよ」的なことが書いてあります)。
が、field経由でv-modelのmodalValue
が上手く渡せていない場合があったため、従来通り入力要素側にv-modelをバインドし、FieldにmodalValue
を直接渡す形にすることでうまく動きました。
ちなみに、この形ならVueTagsInput
などの外部コンポーネントや独自のカスタムコンポーネントでも同様に対応できます。
↓VueTagsInputの例(余計な部分は省略してます)
<VueTagsInput
v-model="typingTag"
@tags-changed="(newTags) => changeTags(newTags)"
>
<template #between-elements>
</template>
<div
slot="autocomplete-item"
slot-scope="props"
>
</div>
</VueTagsInput>
<ValidationField
:model-value="selectedTags.length"
name="タグ"
rules="max_length:10"
v-slot="{ field, errors }"
as="div"
>
<input :="field" type="hidden" />
<p v-if="errors.length">{{ errors[0] }}</p>
</ValidationField>
<ValidationField
:model-value="addingTag"
name="タグのテキスト"
rules="tagMax:30"
v-slot="{ field, errors }"
as="div"
>
<input :="field" type="hidden" />
<p v-if="errors.length">{{ errors[0] }}</p>
</ValidationField>
VueTagsInputにバインドしている入力中のタグテキストや、選択中のタグの配列などをFieldに紐づけて、その値を使ってバリデーションしています。
本来vue-tags-inputもVue3の破壊的変更の影響により移行必須ですが、今回はVue3に対応してForkされたものを暫定採用しています
https://github.com/sipec/vue-tags-input
state
v3でvalidation stateを使ってバリデーションタイミングの制御をしていた場合、v4ではmeta slotに変更になっているのでそちらに置き換えます。
ただ、v4からは監視できる状態が
- touched
- dirty
- valid
- validated
- pending
のみになっています。
as
v3のtag=”div”
はすべてasに置き換わっているのでas=”div”
に置換します。以上。
validate()
v3ではObserverのrefから得られるvalidateWithInfo
を使ってform送信前の全体バリデーションを行っていたのですが、この関数も廃止されているので、代わりにvalidate
に置き換えます。
- this.$refs.observer.validateWithInfo().then(({ isValid, fields }) => {
- this.isValid = isValid;
- this.errorsMessage = [];
- Object.values(fields).filter(({ failedRules }) => {
- if (Object.keys(failedRules).length) {
- this.errorsMessage.push(Object.values(failedRules));
- }
- });
- });
+ this.$refs.observer.validate().then(({ valid, errors }) => {
+ this.isValid = valid;
+ this.errorsMessage = Object.values(errors) ?? [];
+ });
従来はfieldsの中身からmessageを引っ張り出していましたが、validateから得られるerrorsはname: message
のシンプルなオブジェクトになっているので、値をそのまま取り出して突っ込む形に修正します。
また、エラーフラグ名がisValidではなくvalidになっているのでこちらも併せて修正します。
↓ちなみに、v3のvalidateWithInfoから返ってきてたfieldsの中身はこんな感じです。
["$name": {
"id": "hoge",
"name": "$name",
"failedRules": {
"required": "ここにメッセージが入ってます"
"any rule1": "同上",
"any rule2": "同上"
},
"untouched": false,
"touched": true,
"dirty": true,
"pristine": false,
"valid": false,
"invalid": true,
"validated": true,
"pending": false,
"required": true,
"changed": true,
"passed": false,
"failed": true
}]
🦺 最後に
Vue2がEOLになって一年が過ぎようとしていますが、なんとか記事が間に合ったのでよかったです (間に合ってない)
それでは、良いお年を
一緒に二次元業界を盛り上げていきませんか
株式会社viviONでは、フロントエンドエンジニアを募集しています。
また、フロントエンドエンジニアに限らず、バックエンド・SRE・スマホアプリなど様々なエンジニア職を募集していますので、ぜひ採用情報をご覧ください。