😸

Nuxt3でvee-validateとyupでバリテーション 応用編

2024/04/21に公開

下記の記事の応用編になります
初めから説明するので、応用編から入った方は見なくても大丈夫です
https://zenn.dev/sayasurvey/articles/7f28a4fcb2b517

前回から来た方はパッケージのインストールとエラーメッセージの日本語化とカスタムバリテーションの追加はスキップしてください

完成系

パッケージのインストール

npm install vee-validate --save
npm install yup yup-locale-ja
npm install --save @types/lodash

パッケージの説明

パッケージ名 説明
vee-validate バリテーションを簡単に実装できる
yup vee-validateで使用するバリテーションのルールを簡単に実装できる
yup-locale-ja yupのバリテーションメッセージを日本語化
@types/lodash カスタムバリテーションメッセージとyupのバリテーションメッセージを結合するのに使用

エラーメッセージの日本語化とカスタムバリテーションの追加

//plugins/vee-validate.client.ts
import _ from "lodash";
import { setLocale, type LocaleObject } from "yup";
import { suggestive } from "yup-locale-ja";

export default defineNuxtPlugin(() => {
  const customeLocaleObject: LocaleObject = {
    mixed: {
      required: ({ label }) => (label ? label + "は" : "") + "必須項目です", //①
    }
  }

  const LocaleObject = _.merge({}, suggestive, customeLocaleObject); //②

  setLocale(LocaleObject); //③
})

①で既存のバリテーションメッセージを変更しています
既存のバリテーションメッセージは「
{項目名}には値を入力してください」なのでそのままで良い方は設定不要です

②では日本語のバリテーションメッセージとカスタムバリテーションメッセージの設定を統合しています
①を作成していない方は②も作成不要です

③ではバリテーションメッセージの設定を適用しています
①、②を作成していない方はsetLocale(suggestive); に差し替えてください

入力フォームのコンポーネントのファイル作成

アプリのリポジトリ直下にcomponentsリポジトリを作成
その中にTextField.vueファイルを作成
TextFieldの作成範囲は下記の画像の通り

TextField.vueファイルの中身を下記のように記載する

<template>
  <div>
    <Field v-model="props.modelValue" :name="props.name" v-slot="{ field }"> //①
      <p>{{ props.label }}</p>
      <input v-bind="field" :type="props.inputType" /> //②
    </Field>
    <ErrorMessage :name="props.name" v-slot="{ message }"> //③
      <p>Error: {{ message }}</p>
    </ErrorMessage>
  </div>
</template>

<script setup lang="ts">
import { ErrorMessage, Field } from 'vee-validate';
import { defineProps, withDefaults } from 'vue';

interface Props { //④
  modelValue: string,
  label: string,
  name: string,
  inputType?: string
};

const props = withDefaults(
  defineProps<Props>(), {
    inputType: "text" //⑤
  }
);
</script>

コードの解説

① 前回のコードではFieldコンポーネントのみで入力フォームを作成しました
ただ、今回はコンポーネントに切り出した上で、inputタグをFieldコンポーネントから分離させています
v-modelはTextFieldコンポーネントのv-modelで指定した変数等に双方向バインディングして、入力欄の表示や入力した内容を指定した変数にリンクさせることができます
Fieldからinputタグを分離させるにはv-slotディレクティブを使用してfieldというスロットを定義し、②のようにinputタグのv-bindに紐付ける必要があります
nameプロパティに指定する項目は次に作成するapp.vueでバリテーションの条件をまとめたvalidationSchemaに対応するバリテーションエラーを出力することができます

③ inputタグの入力欄に入力した内容がvalidationSchemaで指定した条件と一致しない場合はそれに対応したバリテーションエラーが表示されます
validationSchemaとFieldコンポーネントの紐付けはFieldのnameプロパティとvalidationSchemaで指定したフィールドの名前が一致したものになります

④ app.vueファイルからTextFieldコンポーネントに渡した各プロパティを使用するのにデータの型を定義しています
inputType?のようにプロパティの名前の後に?を付けることでオプショナルな型定義が出来ます。
こうするとinputTypeを指定しなくともundefinedが設定されます

⑤で④の情報を元に親コンポーネントから渡されたデータをpropsに格納しています
inputTypeが設定されていない場合はデフォルト値としてstring型でtextが格納されるようになっています
④でオプショナルな型定義をする必要がなかったと思われる方もいらっしゃるかもしれませんが、オプショナルな型定義を行わない場合は下記のような警告が出てしまうため対応しています

表示するページのファイルを作成

app.vueファイルに下記のコードを記載する

<template>
  <div>
    <Form :validationSchema="validationSchema" v-slot="{ meta: { valid } }"> // ①
      <TextField v-model="email" name="email" label="メールアドレス"/>
      <TextField v-model="password" name="password" label="パスワード" inputType="password"/> //②
      <button :disabled="!valid">ログイン</button> //③
    </Form>
  </div>
</template>

<script setup lang="ts">
import { Form } from 'vee-validate';
import * as yup from 'yup';

const email = ref(""); //④
const password = ref("");

const validationSchema = yup.object().shape({ //⑤
  email: yup.string().required().email().label("メールアドレス"),
  password: yup.string().required().min(8).label("パスワード")
})
</script>

① vee-validateのFormコンポーネントにvalidationSchema変数に設定したバリテーションの条件を設定する
そうする事によってFormの中の要素に対してバリテーションエラーが発生した場合にErrorMessageコンポーネントの部分にエラーが表示される仕組み
meta: { valid }スロットを定義してボタンの活性非活性化のフラグとして使用する

② 先ほど作成したTextFieldコンポーネントの各プロパティにデータを設定する
各データの内容は下記のとおり

プロパティ名 説明
v-model Fieldコンポーネントと双方向バインディングさせたいデータ
name validationSchemaに設定したバリテーションのルールと紐づくField名
label 何の項目の入力欄かを示すためのラベルの内容
inputType inputタグのtypeに設定する内容でpasswordを指定すると伏せ字が画面に表示される

③ Fieldコンポーネントで定義してたvalidがfalseの場合はボタンを非活性にするようにコードを作成

④ 入力欄に双方向バインディングさせるためのリアクティブ型の変数を作成

⑤ yupを使用してバリテーションの条件を変数に設定する
この際、Fieldコンポーネントのnameプロパティの内容と一致させる必要があるので注意が必要
よく使用するバリテーションのルールは下記のサイトを参考にしてください
https://massu-engineer.com/yup-validation-list/

参考サイト

https://vee-validate.logaretm.com/v4/guide/components/validation/
https://tech.andpad.co.jp/entry/2022/12/05/100000

Discussion