2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【vee-validate】v3 → v4マイグレーション【Options API】

Last updated at Posted at 2024-12-24

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のValidationProviderValidationObserverに対応していると考えてもらって大丈夫です。

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関数の引数は、それぞれ

  1. 実際の値
  2. 設定している閾値の配列※
    • なにも設定していない場合はnull
  3. 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になって一年が過ぎようとしていますが、なんとか記事が間に合ったのでよかったです :relaxed:(間に合ってない)

それでは、良いお年を :wave:

:punch: 一緒に二次元業界を盛り上げていきませんか

株式会社viviONでは、フロントエンドエンジニアを募集しています。

また、フロントエンドエンジニアに限らず、バックエンド・SRE・スマホアプリなど様々なエンジニア職を募集していますので、ぜひ採用情報をご覧ください。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?