Nuxt3で登録画面でYupを使ったバリデーション処理を実装する

# サンプル

たとえば、下記のような管理画面があるとしましょう。何も入力しないで「Register」ボタンを押すと...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/5caee0a5-6b64-480e-bd26-79bc3bab19b9.png)
下記の図のようにちゃんとエラーが出ましたね。これは実務でも良く使われるモジュール`Yup(ユップ)`を使ったバリデーションを使用して簡単に実装できますので早速やっていきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1169664/4970b9f9-870a-42b0-84b2-ead6cf42852b.png)

# ディレクトリ構成
```
.
└── Project/
    ├── .nuxt
    ├── .output
    ├── components/
    │   └── navbar/
    │       └── navbar.vue
    ├── node_modules
    ├── pages/
    │   ├── adminUsers
    │   ├── generalUsers/
    │   │   └── registration/
    │   │       └── registration.vue
    │   ├── index.vue
    │   ├── public/
    │   │   ├── favicon.ico
    │   │   └── robots.txt
    │   └── server/
    │       └── tsconfig.json
    ├── app.vue
    ├── nuxtconfig.ts/
    │   ├── package.json
    │   └── package-lock.json
    ├── README
    └── tsconfig.json
```

# 事前準備(`Yup`をインストール)
`Yup`は下記の公式サイトからインストールできます。

https://www.npmjs.com/package/yup

```
npm i yup
```

# 実装

`Yup`をインポートします。

```registration.vue
import * as yup from 'yup';
```

`yup`を使ったバリデーションは下記のように書いていきます。

```registration.vue
const nameField = useField(
  'name',
  yup.string().required('Name must be required.').min(2,'Please input over 1 character.'),
);
const {value:name,errorMessage:nameerror} = nameField;
```

Formの送信処理はこちら↓
```registration.vue
import {useField,useForm} from 'vee-validate';
const {handleSubmit} = useForm();
const onSubmit = handleSubmit(() => {
  console.log('送信されたフォームデータ:', {
    name: name.value,
    email: email.value,
    password: password.value,
    gender: gender.value,
    survey: survey.value,
  });
});
<v-form @submit.prevent="onSubmit" ref="formRef">
    //処理...
</v-form>
```

全体のコードはこちら↓
```pages/generalUsers/registration/registration.vue
<script setup lang="ts">
import {ref} from 'vue';
import {useField,useForm} from 'vee-validate';
import * as yup from 'yup';

const formRef = ref(null);
const {handleSubmit} = useForm();

/*---Name validation---*/
const nameField = useField(
  'name',
  yup.string().required('Name must be required.').min(2,'Please input over 1 character.'),
);
const {value:name,errorMessage:nameerror} = nameField;

/*---Email validation---*/
const emailField = useField(
  'email',
  yup.string().required('Email must be required.').matches(/^[a-zA-Z0-9_\-]+(\.[a-zA-Z0-9_\-]+)*@([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.)+[a-zA-Z]{2,}$/
,{message:'Mail address is invalide'}),
);
const {value:email,errorMessage:emailerror} = emailField;

/*---Password validation---*/
const passwordField = useField(
  'password',
  yup.string().required('Password must be required.').min(2,'Please input over 1 character.').max(11,'The password is too long.Please input until 10 charactoers.'),
);
const {value:password,errorMessage:passworderror}=passwordField;

/*---Gender validation---*/
const genderField = useField(
  'gender',
  yup.string().required('Gender must be required.'),
);
const {value:gender,errorMessage:gendererror} = genderField;

/*---Survey validation---*/
const surveyField = useField(
  'survey',
  yup.string().required('Please choose at least one.'),
);
const { value: survey, errorMessage: surveyerror } = surveyField;
//const survey = ref('');


const surveyOptions = ['I want to know from News letters', 'I want to know from Campain', 'I want to know from Events']

const onSubmit = handleSubmit(() => {
  console.log('送信されたフォームデータ:', {
    name: name.value,
    email: email.value,
    password: password.value,
    gender: gender.value,
    survey: survey.value,
  });
});

const router = useRouter();
const clickBackToTopPage = ()=>{
    router.push('/');
}

</script>
<style scoped>
p{
  color: red;
}
</style>
<template>
      <v-app>
    <v-main>
      <v-container class="mt-5">
        <v-form @submit.prevent="onSubmit" ref="formRef">
          <!-- Name -->
          <v-text-field
            v-model="name"
            label="Name"
            required
          ></v-text-field>
          <p>{{nameerror}}</p>

          <!-- Mail Address -->
          <v-text-field
            v-model="email"
            label="Email"
            type="email"
            required
          ></v-text-field>
          <p>{{emailerror}}</p>
          <!-- Password -->
          <v-text-field
            v-model="password"
            label="Password"
            type="password"
            required
          ></v-text-field>
          <p>{{passworderror}}</p>
          <!-- Sex（radio Button） -->
          <v-radio-group
            v-model="gender"
            label="Sex"
            row
          >
            <v-radio label="man" value="man"></v-radio>
            <v-radio label="woman" value="woman"></v-radio>
            <v-radio label="other" value="other"></v-radio>
          </v-radio-group>
          <p>{{gendererror}}</p>
          <!-- アンケート（チェックボックス） -->
          <v-checkbox
            v-for="option in surveyOptions"
            :key="option"
            :label="option"
            :value="option"
            v-model="survey"
          ></v-checkbox>
          <p>{{surveyerror}}</p>
          <!-- 送信ボタン -->
          <v-btn type="submit" color="primary" class="mr-4">Register</v-btn>
          <v-btn type="button" color="success" @click="clickBackToTopPage">BackToTopPage</v-btn>
        </v-form>
      </v-container>
    </v-main>
  </v-app>
</template>
```

以上です。

# サイト

https://zenn.dev/tsuboi/articles/77f9ae4ab9e57f

https://www.greenwich.co.jp/blog-archives/p/31717

https://regex101.com/

https://qiita.com/Cut/items/8acbdd0e2b6687b3cb9f

https://qiita.com/str32/items/a692073af32757618042

https://qiita.com/kazuma_M/items/5e37d140ae6a86e1f09d

https://qiita.com/shin_k_2281/items/00174a6b754f31de9f37

https://www.npmjs.com/package/vee-validate