引き続きNuxt3のトピックです。
Vue3のUIフレームワークであるvee-validateの組み合わせでフォームのバリデーションを組んでみます。というのも、vee-validate4の使用について少しハマってしまったので、その備忘録をかねて記述しています。
インストールと設定
Nuxt3とvee-validate4をインストールします。
1 2 3 4 5 |
npx nuxi init nuxt-app cd nuxt-app npm install npm i vee-validate @vee-validate/i18n @vee-validate/rules npm install --dev sass |
今回は、i18nとルール、それからsassも一緒に入れておきます。
また、プラグイン定義でルールやロケールを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
import { localize, setLocale } from "@vee-validate/i18n"; import en from "@vee-validate/i18n/dist/locale/en.json"; import ja from "@vee-validate/i18n/dist/locale/ja.json"; import AllRules from "@vee-validate/rules"; import { defineRule, configure } from "vee-validate"; import { defineNuxtPlugin } from "#app"; export default defineNuxtPlugin((_nuxtApp) => { configure({ generateMessage: localize({ en, ja, }), }); Object.keys(AllRules).forEach((rule) => { defineRule(rule, AllRules[rule]); // 全ルールを使えるようにする }); setLocale("ja"); }); |
Fieldのバリデーション
実際の使用例を見てみてます。今回の記事では、Composable APIを使うケースを想定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<script lang="ts" setup> import { useField } from "vee-validate"; const { value: username, errorMessage: usernameError } = useField( "username", "required|min:2", // バリデーションルールを文字列形式で指定 ); const { value: email, errorMessage: emailError } = useField( "email", "required|email", // バリデーションルールを文字列形式で指定 ); const submit = () => { console.log(username.value, email.value); }; </script> <template> <div class="form"> <div class="field"> <label for="username">username</label> <input type="text" v-model="username" name="username" /> <small v-if="usernameError" ><span class="invalid">{{ usernameError }}</span></small > </div> <div class="field"> <label for="email">email</label> <input type="text" v-model="email" name="email" placeholder="admin@example.com" /> <small v-if="emailError" ><span class="invalid">{{ emailError }}</span></small > </div> <div class="field"> <button type="button" @click="submit">登録</button> </div> </div> </template> <style lang="scss" scoped> html { font-size: 14px; } .form { .field { padding: 0.5rem 0; } input { width: 15rem; display: block; } label { display: block; font-weight: bold; } .invalid { color: red; } } </style> |
useFieldでフォームフィールドごとの定義をしています。useFieldは、valueやerrorMessageといったオブジェクトを返します。
実際に動かした画面は以下のようになります。
Formのバリデーション
続いてFormのケースです。useFormというComposable APIを使います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<script lang="ts" setup> import { useForm, useField } from "vee-validate"; const { handleSubmit, errors } = useForm({ validationSchema: { username: "required|min:2", email: "required|email", }, }); const { value: username } = useField("username"); const { value: email } = useField("email"); const submit = handleSubmit(() => { console.log(username.value, email.value); }); </script> <template> <div class="form"> <div class="field"> <label for="username">username</label> <input type="text" v-model="username" name="username" /> <small v-if="errors.username" ><span class="invalid">{{ errors.username }}</span></small > </div> <div class="field"> <label for="email">email</label> <input type="text" v-model="email" name="email" placeholder="admin@example.com" /> <small v-if="errors.email" ><span class="invalid">{{ errors.email }}</span></small > </div> <div class="field"> <button type="button" @click="submit">登録</button> </div> </div> </template> <style lang="scss" scoped> html { font-size: 14px; } .form { .field { padding: 0.5rem 0; } input { width: 15rem; display: block; } label { display: block; font-weight: bold; } .invalid { color: red; } } </style> |
useFieldで定義していたバリデーションをuseFormで指定しています。useFieldは、useFormのformを自動的に検出してくれます。実際には、provide/injectの仕組みが内部で使用されており、useFormやuseFieldの呼び出し時にそれぞれ固有のSymbolでコンテキストがprovideされています。useFieldは、useFormでprovideされたformコンテキストを自身のコンポーネント内でinjectしています。そのため、上記の例で言うと、useFieldを先に呼び出し、useFormを後で呼び出すとuseFieldがformコンテキストを探せず期待した動きにならないことがあります(私自身ややハマってしまった)。初回表示時にuseFormがfieldを見つけられずエラーと扱ってしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<script lang="ts" setup> import { useForm, useField } from "vee-validate"; const { value: username } = useField("username"); const { value: email } = useField("email"); const { handleSubmit, errors } = useForm({ validationSchema: { username: "required|min:2", email: "required|email", }, }); const submit = handleSubmit(() => { console.log(username.value, email.value); }); </script> |
実際に上記例を動かすと、ページ表示時にバリデーションエラーが表示されてしまい期待した動きになりませんでした。
まとめ
Nuxt3とvee-validateの組み合わせを試してみました。Composable APIで簡単にフォームバリデーションを導入することができます。
本サンプルは以下になります。
参考リンク
- https://vee-validate.logaretm.com/v4/
vee-validateの公式サイトです。 - https://github.com/logaretm/vee-validate
vee-validateのGitリポジトリです。 - https://reffect.co.jp/vue/veevaliate4
使い方がとても詳しく説明されており大変参考になります。
コメント