BLOG

ブログ

Nuxt3でVeeValidate4とyupを使ってバリデーションを実装してみた

はじめに

こんにちは、遠藤です!
今回はNuxt3でカスタムバリデーションを実装していきます!

バリデーションはIDやパスワードなど文字を入力欄に誤りがあると教えてくれる便利な機能、簡単にいうと入力チェックのことです!

実装にはいくつか方法がありますが、今回はNuxt3専用のバリデーションライブラリであるVeeValidate4とルールの定義を簡単にしてくれるライブラリyupを使用して実装してみたいと思います! 是非記事を読みながら実際に手を動かして、一緒にバリデーションの実装をお試しください!!

この記事でわかること

  • Veevalidate4の関数 useFielduseFormの使い方
  • yupでバリデーションルールを作る方法
  • yupの応用(datepickerにバリデーションを実装します)

最後まで読んでいただけますと嬉しいです!
それではよろしくお願いいたします!

基本設定

まずは開発環境を用意します!

下記コマンドでNuxtの環境を用意します。任意のディレクトリで以下のコマンドを実行し、Nuxtの環境を新規作成します。その後cdで作成したNuxtのディレクトリに移動します。

npx nuxi@latest init validation-sample
cd validation-sample

次に下記コマンドでveevalidateとyupをそれぞれinstall しましょう。

yarn add vee-validate yup

以下の写真のようにpackage.jsonにVeeValidateとyupの記載が追加されていれば正常にインストールされています。

次にバリデーションを実装するためのpagesディレクトリとその中身を作成しておきます。
プロジェクトのルート直下にpagesディレクトリを新規作成後、pagesディレクトリ内にindex.vueファイルを追加します。

index.vueファイルの中身は以下のように記述します。 input要素に文字を入力するとvalueの値が動的に変わるように設定します。

<script setup>
const value = ref()

</script>

<template>
  <h1>バリデーションサンプル</h1>
  <input type="text" v-model="value">
  <p>{{ value }}</p>
</template>

最後にapp.vueを以下のように書き換えます。

<template>
  <div>
    <NuxtPage />
  </div>
</template>

上記設定が終わったら下記コマンドでローカル環境での動作を確認します。

yarn dev

ブラウザでlocalhostにアクセスし、以下のような画面が表示されれば準備完了です!
input要素に入力した文字がテキストフィールドの下に表示されるかも一緒に確認しましょう!

それでは実際にVeeValidateとyupを使用して簡単お手軽にバリデーション実装しましょう!

VeeValidateを使用する

まずはVeeValidateのみを使用して初歩的なバリデーションを実装します。

VeeValidateとは

VeeValidateはNuxt専用のバリデーションライブラリです。ライブラリの関数を使用することで簡単にバリデーションを実装することができます。 Nuxt2の時はVeeValidate3というライブラリを使用していましたが、Nuxt3になり仕様が大きく変更したことで、ライブラリもアップデートされVeeValidate4になりました。 Nuxt3でVeeValidate3を使用することはできないので注意が必要です。また、公式のドキュメントもVeeValidate3VeeValidate4に分かれているのでご注意ください。

それでは実際に使用してみます!

useField関数を使用する

まずはindex.vueページのscriptタグ内で、VeeValidateからuseField関数を呼び出します。 そして以下のように定数valueの値の部分を書き換えます。

<script setup>
import { useField } from 'vee-validate';
const { value } = useField("sample")

</script>

<template>
  <h1>バリデーションサンプル</h1>
  <input type="text" v-model="value">
  <p>{{ value }}</p>
</template>

Nuxt3では、動的に変化する定数をref関数を使用して定義していました。

VeeValidateを使用する場合、バリデーションで制御したい動的に変化する定数はuseField関数を使用して定義します。useFieldの定義については後述しますが、第一引数にfield名、第二引数に関数をとります。

イメージとしては、useField = ref + バリデーション機能という感覚です。(個人の見解です)

まだバリデーション機能を設定していないので、このままだとrefの関数と同じような動きをします。ためしにテキストフィールドに文字を入力すると、そのまま入力した文字が表示されます。

では実際にバリデーション機能を設定していきます!

バリデーション機能の実装

useFieldの第二引数に以下のように関数を追記します。 また、valueのほかにエラー時にuseField関数からメッセージを受け取るerrorMessageを定数に追記します。そしてp要素の中身をerrorMessageに書き換えます。

<script setup>
import { useField } from 'vee-validate';
const { value,errorMessage } = useField("sample",(value) => {
  if (!value) {
    return 'this field is required';
  }
  return true;
})

</script>

<template>
  <h1>バリデーションサンプル</h1>
  <input type="text" v-model="value">
  <p>{{ errorMessage }}</p>
</template>

テキストフィールドに入力後、すべての文字を消した際に”this field is required”と表示されていればバリデーションが実装できています!!

useField関数を使用すれば、定型に縛られない自由なカスタムバリデーションを簡単に作成することができます!ですが、条件に合わせて都度ルールを作成するのはなかなか大変な作業です。

そこで登場するのがyupです!

yupを使用する

yupはemail判定や文字数制限など、開発者側が使用するであろうルールをまとめてJavaScriptの形式で呼び出せるライブラリです。

本記事の最後にも紹介しますが、yupの定型ルールを使用しながら独自の関数で条件を追加することも可能なのでカスタマイズ性にも非常に優れています!

まずはyupを使用してみましょう!以下の記述でライブラリからyupで使用する関数をすべて呼び出します。そして先ほどuseFieldの第二引数に設定した関数をyupに書き換えます。

<script setup>
import { useField } from 'vee-validate';
import * as yup from 'yup';

const { value,errorMessage } = useField("sample",
yup.string().required("この項目は必須です"))

</script>

テキストフィールドを空欄にした時、”この項目は必須です”と表示されていればyupが正しく使用できています。指定した条件(ここではrequired)に引数としてメッセージを設定することで、エラー時に表示するメッセージをそれぞれ設定できます。
引数を設定しない場合はyupのデフォルト文言が表示されます。デフォルトの文言は設定で変更できるので後述します!お楽しみに!

yup.string().required()について詳しく解説します。

yupを使用する際は、まず初めにどのようなデータにバリデーション機能を適用するか設定する必要があります。今回だと”.string()”の部分です。ここでは文字列を対象にバリデーションが機能するよう設定しています。データ定義には主に下記の定義を使用します。

string()文字列
number()数字
boolean()真偽値
date()日付
array()配列
object()オブジェクト

データの型を定義したあとは詳細な条件を設定していきます。

ここでは”.required()”で必須条件を設定しています。他にもよく使用するものだと、”.email()”や”.min()”や”.max()”などがあります。どのようなルールが用意されているかはyupの公式githubで確認できますので、是非確認してみてください!

複数のinput要素について

実際バリデーションを使用する場合は複数のinput要素が必要になる場合が多いです。ここでは名前とメールアドレスを入力するinput要素を用意し、それぞれに異なるバリデーションを設定します。

まずは定義したvalue と errorMessageに識別するための名称を追記します(分割代入)。次にyupで名前とメールアドレスそれぞれのルールを設定していきます。

今回は下記のようなルールを設定しました。

名前 : 必須、8文字以上
メールアドレス : 必須、メールアドレスの形式

続いてtemplate内の内容を上記に合わせて変更します。 v-model にvalueで設定したそれぞれの値を、p要素にはそれぞれエラーメッセージが表示されるように変更します。

<script setup>
import { useField } from 'vee-validate';
import * as yup from 'yup';

const { value: name, errorMessage: nameError } = useField(
  'name',
  yup.string().required("この項目は必須です").min(8,"8文字以上で入力してください")
);
const { value: email, errorMessage: emailError } = useField(
  'email',
  yup.string().required("この項目は必須です").email("メールアドレスの形式で入力してください")
);
</script>

<template>
  <h1>バリデーションサンプル</h1>
  <input type="text" v-model="name">
  <p>{{ nameError }}</p>
  <input type="text" v-model="email">
  <p>{{ emailError }}</p>
</template>

画像のようにそれぞれ設定した条件に基づきエラーメッセージが表示されていれば正しく機能しています!

useForm関数を使用する

ここまではuseFieldを使用しバリデーションを個別に作成しましたが、VeeValidate4のuseForm関数を使用することで、個々のバリデーションをまとめて、一つのバリデーション処理にすることができます。

まずは実際に二つのバリデーションを一つにまとめてみましょう!

useForm関数を使用する場合、最初にスキーマを定義する必要があります。スキーマはバリデーションのルールを格納する箱のようなイメージです。

schemaの中身について、まずはyup.objectでルールの対象がオブジェクトであることを宣言します。次に先ほど設定したルールをそれぞれ設定します。ここで設定するプロパティはuseField関数の第一引数と結びつきます。schemaがバリデーション全体の名前で、nameやemailのようなプロパティが各バリデーションの名称というイメージです。

作成したスキーマにuseForm関数を使用して、ルールとして使用できるようにします。

その後useField関数で、バリデーションで制御したい定数を定義します。useForm関数ですでにルールを定義しているため、useField関数には第一引数のルール名のみ(スキーマで定義した各プロパティ名)を渡します。※スキーマのプロパティ名とuseField関数の第一引数が合致しない場合、コンソールにエラーは出ませんが不自然な挙動をするので注意です。

const schema = yup.object({
  name: yup
    .string()
    .required("この項目は必須です")
    .min(8, "8文字以上で入力してください"),
  email: yup
    .string()
    .required("この項目は必須です")
    .email("メールアドレスの形式で入力してください"),
});

const { errors } = useForm({
  validationSchema: schema,
});

const { value: name } = useField('name');
const { value: email } = useField('email');

ここでuseForm関数から戻される項目には、スキーマで定義したプロパティがそれぞれ含まれています。今回はわかりやすくerrorsのみ設定しています。templateでerrors.nameやerrors.emailのように呼び出すとそれぞれのエラーメッセージを取得できます。

<template>
  <h1>バリデーションサンプル</h1>
  <input type="text" v-model="name">
  <p>{{ erros.name }}</p>
  <input type="text" v-model="email">
  <p>{{ errors.name }}</p>
</template>

input要素に入力した際、これまでと同様にバリデーションメッセージが表示されていればuseFormを正しく使用できています!

なぜバリデーションをまとめるのか

ここではuseForm関数を使用して、複数のルールを持つバリデーション処理を作成しましたが、私自身、どうして処理を統合する必要があるのか、最初はわからなかったので少しだけ解説します!

これまでは作成したのはそれぞれ機能を持った名前バリデーションやメールバリデーションです。 個々のバリデーションがそれぞれエラーを返します。この場合、バリデーションエラーがある時、それぞれのエラーを参照して次の処理を進める必要があります。

ですが実際の実装を考える場合、一つ一つのエラーに合わせて処理を分岐させるのは手間がかかります。そこで二つのルールを組み合わせたバリデーションを作成し、どれか一つでも一致しないものがある時、その処理全体としてエラーを返すことで、バリデーションエラー処理後の操作をシンプルにすることができます。

この辺りは実際にformを作成する際に、input内のデータ処理をしていく中でその意味を実感しました!ですのでまずは手を動かすのが大事だと思っています!ぜひお手元のPCでいろいろな実装を試してみてください!!

共通設定とオーバーライド

ここでは共通設定について確認していきます。yupの各ルールに任意の文字列を引数として与えない場合は、デフォルトの文言が表示されます。デフォルトの文言は英語です。

このデフォルトの文言はsetlocale関数を使用することであらかじめ設定することができます。
早速コードを書いてみます!!

import { setLocale } from 'yup'

setLocale({
  mixed: {
    required: "この項目は必須です",
  },
  string: {
    email: "メールアドレスの形式ではありません。",
    min: ({ label }) => `${label}文字以上で入力してください`,
  },
});

const schema = yup.object({
  name: yup.string().required().min(8).label("8"), //.label()を追加
  email: yup.string().required().email(),
});

ここでは、

・required
・string – email
・string – min

へデフォルト文言をそれぞれ設定しています。 設定する文言は動的に変更することができます。今回は”.min()”について、”.label()”の引数を設定することで、任意の文字数を表示できるようにしました。

上記の状態でinput要素に文字を入力してみます。これまでの実装と変わらずに日本語でエラーが表示されていれば、setLocale関数が適切に機能しています。

このsetLocale関数は他のページでバリデーションを使用する時にも反映させたいので、pluginsに記述して共通化します。ルート直下にpluginsディレクトリを作成し、setLocale.tsを作成します。 下記のようにsetLocale関数の記述を転記します。

import { setLocale } from 'yup'

export default defineNuxtPlugin(() => {
    setLocale({
        mixed: {
          required: "この項目は必須です",
        },
        string: {
          email: "メールアドレスの形式ではありません。",
          min:({ label }) => `${label}文字以上で入力してください`
        },
      });
})

pluginsで設定したsetLocale関数の内容は、呼び出し元でオーバーライドすることができます!

pluginsでsetLocale関数を設定した状態で、下記のようにindex.vueのスキーマ部分で定義したルールに任意の文言を引数として渡します。”.required()”のエラーメッセージがそれぞれ異なっているのがわかります。(上がオーバーライド、下がsetLocale)

const schema = yup.object({
  name: yup.string().required("必ず入力してください").min(8).label("8"),
  email: yup.string().required().email(),
});

一部のページでバリデーションの文言を変更したい時でも簡単に対応することができるので便利です!

datepicerにバリデーション

最後に応用の例として、外部ライブラリから呼び出したdatepickerにバリデーションを設定した例をご紹介します。今開始日と終了日を指定するdatepickerがそれぞれあり、終了日の日付が開始日より前の日付の場合にバリデーションメッセージを表示します。

完成形の画面とコードがこちらです。

yupの”.test()”は自ら条件を設定することができるため、開始日と終了日を比較して条件を満たさない場合にバリデーションメッセージを表示することができます。

<script setup>
import * as yup from "yup";
import Datepicker from "@vuepic/vue-datepicker";
import "@vuepic/vue-datepicker/dist/main.css";

const schema = yup.object({
  startDate: yup.date().required(),
  endDate: yup
    .date()
    .required()
    .test(
      "endData_validate",
      "開始日以降の日付を選択してください",
      () => endDate.value > startDate.value
    ),
});

const { errors, handleSubmit } = useForm({
  validationSchema: schema,
});

const { value: startDate } = useField("startDate");
const { value: endDate } = useField("endDate");
</script>

<template>
  <h1>バリデーションサンプル</h1>
  <div class="width">
    <p>開始日</p>
    <Datepicker
      v-model="startDate"
      format="yyyy年MM月dd日 HH:mm"
      select-text="決定"
      cancel-text="キャンセル"
      class="datepicker"
    />
  </div>
  <div class="width">
    <p>終了日</p>
    <Datepicker
      v-model="endDate"
      format="yyyy年MM月dd日 HH:mm"
      select-text="決定"
      cancel-text="キャンセル"
      class="datepicker"
    />
  </div>
  <p>{{ errors.endDate }}</p>
</template>

※styleは省略しています。

“.test()”は第一引数はルール名、第二引数は表示する文言、第三引数に関数を指定することで、オリジナルの条件を設定することができます。
今回の場合、開始日と終了日を比較して条件を満たさない場合にバリデーションメッセージを表示することができるようになります。

yupで定義されていないルールは自前で作ることができ、他のルールと組み合わせられる点が非常に便利です!

まとめ

いかがでしたでしょうか?

VeeValidate4とyupを使用した感想としては、ルールをわかりやすく並べて定義できるのが使いやすいなと思いました!また、最後に紹介したように、既存のルールと自作したルールを簡単に結びつけられたり、共通化したものをオーバーライドできたりと、非常に使い勝手が良く開発しやすいライブラリだなと感じています!

最後までお読みいただきありがとうございました!
Nuxt3でバリデーション実装の際は是非、VeeValidate4とyupをお試しください!!

それでは!!!!