🐥

vee-validate(useForm とuseField)でバリデーションした時の備忘録

2023/08/22に公開

Register.vueファイルに実装した時を例に書いてます。
自分のファイルに置き換えながら進めてください。

main.ts

main.tsに以下を書くことでグローバルでバリデーション使える様に設定してます。

main.ts
// vee-validateバリデーションの設定
import { defineRule, configure } from 'vee-validate'
import { localize } from '@vee-validate/i18n'
import ja from '@vee-validate/i18n/dist/locale/ja.json'
import AllRules from '@vee-validate/rules'

Object.keys(AllRules).forEach((rule) => {
  defineRule(rule, AllRules[rule])
})

configure({
  generateMessage: localize({
    ja
  })
})

localize('ja')
// ここまでvee-validate
  • AllRulesで全てのルールを有効化
  • vee-validate/i18nでエラーメッセージを日本語化

Register.vue

  • インポートはこの2つ
    import { useField, useForm } from 'vee-validate'
    import { object, string, setLocale } from 'yup'
    yupはバリデーション用のライブラリ。

useFieldでinputタグと紐付け

const { value: department } = useField('department')
const { value: email } = useField('email')
const { value: password } = useField('password')
  • useFieldは特定のフォームフィールドのバリデーション状態と関連する情報を取得できます。第1引数=フィールド名、第2引数=バリデーション関数が入ります。今回はバリデーションは別で(yupで)つけるので第2引数は不要です。valueはv-modelの値です。

useFormでフォーム全体にバリデーションを作用させる

const { errors } = useForm({
  validationSchema: schema,
  initialValues: {
    department: '',
    email: '',
    password: '',
  }
})
//エラーメッセージの表示はtemplateの中で以下のように使える
<p class="text-red-500">{{ errors.email }}</p>
  • useFormを使うことで複数の入力箇所に一括でバリデーションをつけられます。イメージは、useFieldは1つのiuputに対して作用。useFormは(複数のiuputまとめてる)formタグ対して作用するイメージ。
  • initialValueは必要なら記載。
  • validationSchemaにschemaを設定することでバリデーションができる。

↓schemaの設定は次の通り

schemaでつけたいバリデーションを設定する

const schema = object({
  department: string().required().label('所属区分'),
  email: string().required().email().label('メールアドレス'),
  password: string().required().label('パスワード').min(4),
})
  • schemaで各inputに対してつけたいバリデーションを設定します。
    string()=文字列のバリデーション
    required()=必須入力のバリデーション
    email()=メールアドレス形式のバリデーション
    min()=最低文字数のバリデーション

↓なんのバリデーションがあるのかは以下の記事を参照
https://zenn.dev/longbridge/articles/04f40422501348

setLocaleでエラーメッセージの設定

setLocale({
  mixed: {
    default: '不正な値です。',
    required: ({ label }) => `${label}は必須の項目です。`
  },
  string: {
    email: ({ label }) => `${label}の形式ではありません。`,
    min: ({ label, min }) => `${label}${min}以上入力してください。`
  }
})
  • required: ({ label }) => ${label}は必須の項目です。であれば入力がなかった場合、「所属区分(labelの文字)は必須の項目です。」とメッセージが表示される。

  • mixedはyupのlocale.d.tsファイルのMixedLocaleを指しています。stringならStringLocale。どのLocaleになんのバリデーションがあるかも、上記の記事を参照するとわかりやすいです。

  • エラーメッセージの設定はsetLocaleを使わなくてもschemaの中に直接設定することもできます。department: string().required('所属区分は必須の項目です。').label('所属区分'),required()みたいに()の中にメッセージを設定。

作業は終わりです!
templateは以下を参照してください。

コード例(ユーザー登録フォーム)

main.ts
// import './assets/main.css'

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import '@/index.css'
import { library } from '@fortawesome/fontawesome-svg-core'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import {
  faEye,
  faEyeSlash,
  faDesktop,
  faMobileScreenButton
} from '@fortawesome/free-solid-svg-icons'
library.add(faEye, faEyeSlash, faDesktop, faMobileScreenButton)

// vee-validateバリデーションの設定
import { defineRule, configure } from 'vee-validate'
import { localize } from '@vee-validate/i18n'
import ja from '@vee-validate/i18n/dist/locale/ja.json'
import AllRules from '@vee-validate/rules'

Object.keys(AllRules).forEach((rule) => {
  defineRule(rule, AllRules[rule])
})

configure({
  generateMessage: localize({
    ja
  })
})

localize('ja')
// ここまでvee-validate

const app = createApp(App)

app.use(router).component('font-awesome-icon', FontAwesomeIcon)

app.mount('#app')

Register.Vue
<script setup lang="ts">
import WhiteButton from '@/components/ui/button/WhiteButton.vue'
import { ref } from 'vue'
import { useField, useForm } from 'vee-validate'
import { object, string, setLocale } from 'yup'

let isShow = ref('password')

// パスワード表示切り替え
const onClick = () => {
  if (isShow.value === 'password') {
    isShow.value = 'text'
  } else if (isShow.value === 'text') {
    isShow.value = 'password'
  }
}

setLocale({
  mixed: {
    default: '不正な値です。',
    required: ({ label }) => `${label}は必須の項目です。`
  },
  string: {
    email: ({ label }) => `${label}の形式ではありません。`,
    min: ({ label, min }) => `${label}${min}以上入力してください。`
  }
})

const schema = object({
  lastName: string().required().label('姓名'),
  firstName: string().required().label('姓名'),
  hireDate: string().required().label('入社月'),
  department: string().required().label('所属区分'),
  // email: string().required().email().label('メールアドレス'),
  password: string().required().label('パスワード').min(4),
  confirmPassword: string().required().label('確認パスワード').min(4)
})

const { errors, handleSubmit, isSubmitting } = useForm({
  validationSchema: schema,
  initialValues: {
    lastName: '',
    firstName: '',
    hireDate: '',
    department: '',
    email: '',
    password: '',
    confirmPassword: ''
  }
})

const { value: lastName } = useField('lastName')
const { value: firstName } = useField('firstName')
const { value: hireDate } = useField('hireDate')
const { value: department } = useField('department')
const { value: email } = useField('email')
const { value: password } = useField('password')
const { value: confirmPassword } = useField('confirmPassword')

const onSubmit = handleSubmit((values, { resetForm }) => {
  console.log(values)
  resetForm()
})
</script>

<template>
  <body>
    <div class="border-2 border-darkBlue w-2/5 mx-auto my-44 rounded-md">
      <h1 class="title text-3xl text-center my-10">新規ユーザー登録画面</h1>
      <form @submit="onSubmit" class="text-center">
        <div class="mb-2">
          <label for="lastName">&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;</label>
          <input
            id="lastName"
            type="text"
            placeholder=""
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
            v-model="lastName"
          />
          <p class="text-red-500">{{ errors.lastName }}</p>
        </div>
        <div class="mb-2">
          <label for="firstName">&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;</label>
          <input
            id="firstName"
            type="text"
            placeholder=""
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
            v-model="firstName"
          />
          <p class="text-red-500">{{ errors.firstName }}</p>
        </div>
        <div class="mb-2">
          <label for="hireDate">&emsp;&emsp;&emsp;入社年月&emsp;</label>
          <input
            id="hireDate"
            type="month"
            placeholder=""
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
            v-model="hireDate"
          />
          <p class="text-red-500">{{ errors.hireDate }}</p>
        </div>
        <div class="mb-2">
          <label for="department">&emsp;&emsp;&emsp;所属区分&emsp;</label>
          <select
            id="department"
            v-model="department"
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
          >
            <option selected value="">所属区分を選択してださい</option>
            <option value="java">Java</option>
            <option value="php">PHP</option>
            <option value="cl">CL</option>
            <option value="ml">ML</option>
            <option value="fr">FR</option>
            <option value="qa">QA</option>
          </select>
          <p class="text-red-500">{{ errors.department }}</p>
        </div>
        <div class="mb-2">
          <label for="email">メールアドレス&emsp;</label>
          <input
            disabled
            id="email"
            type="email"
            placeholder="power.taro@rakus-partners.co.jp"
            class="w-1/2 h-9 mx-auto text-sm px-2"
            v-model="email"
          />
          <p class="text-red-500">{{ errors.email }}</p>
        </div>
        <div class="mb-2">
          <label for="email">&emsp;&emsp;パスワード&emsp;</label>&emsp;&emsp;
          <input
            id="password"
            :type="isShow"
            placeholder=""
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
            v-model="password"
          /><button type="button" @click="onClick">表示</button>
          <p class="text-red-500">{{ errors.password }}</p>
        </div>
        <div class="mb-2">
          <label for="confirmPassword">確認パスワード&emsp;</label>
          <input
            id="confirmPassword"
            type="password"
            placeholder=""
            class="border border-gray-800 rounded w-1/2 h-9 mx-auto text-sm px-2"
            v-model="confirmPassword"
          />
          <p class="text-red-500">{{ errors.confirmPassword }}</p>
        </div>
        <WhiteButton
          type="submit"
          :disabled="isSubmitting"
          label="登録"
          class="w-2/3 my-7 mx-auto text-blue-500 font-bold text-sm rounded-md"
        />
      </form>
    </div>
  </body>
</template>

Discussion