Nuxt3でvee-validate4(Composable API)を使う

data codes through eyeglasses NuxtJS
Photo by Kevin Ku on Pexels.com
この記事は約9分で読めます。

引き続きNuxt3のトピックです。

Vue3のUIフレームワークであるvee-validateの組み合わせでフォームのバリデーションを組んでみます。というのも、vee-validate4の使用について少しハマってしまったので、その備忘録をかねて記述しています。

インストールと設定

Nuxt3とvee-validate4をインストールします。

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も一緒に入れておきます。

また、プラグイン定義でルールやロケールを設定します。

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を使うケースを想定しています。

<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を使います。

<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を見つけられずエラーと扱ってしまいます。

<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で簡単にフォームバリデーションを導入することができます。

本サンプルは以下になります。

GitHub - moritetu/nuxt3-vee-validate-sample: vee-validate example with nuxt3
vee-validate example with nuxt3. Contribute to moritetu/nuxt3-vee-validate-sample development by creating an account on ...

参考リンク

コメント

タイトルとURLをコピーしました