Skip to content

バリデーションルール

15.2.1. クライアント検証戦略

クライアント側のバリデーションはリアルタイム検証送信時検証の2つの戦略に区分します。

戦略トリガータイミング適用対象
リアルタイム検証@blurまたはwatchメールアドレス形式、パスワード強度、必須フィールド
送信時検証@submit.preventフォーム全体の一括検証、フィールド間の交差検証
  • ユーザー体験のためにリアルタイム検証をデフォルト戦略として使用します。
  • フィールド間の依存関係がある検証(パスワード確認など)は送信時検証と併用します。
  • @inputイベントでのリアルタイム検証は過度な呼び出しを引き起こすため使用しません。@blurイベントを使用します。

15.2.2. 必須/形式/範囲検証

検証タイプは以下の3種類に分類し、各タイプに適した検証関数を作成します。

タイプ説明
必須検証値の存在有無空文字列、nullundefinedのチェック
形式検証値のパターン一致メールアドレス、電話番号、URL形式
範囲検証値のサイズ/長さ制限最小8文字、最大100文字、1~999の範囲
typescript
// src/utils/validators.ts
export function required(value: string): string | null {
  return value.trim() === '' ? '必須入力項目です。' : null
}

export function email(value: string): string | null {
  const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return pattern.test(value) ? null : '正しいメールアドレス形式ではありません。'
}

export function minLength(min: number) {
  return (value: string): string | null => {
    return value.length < min ? `最低${min}文字以上でなければなりません。` : null
  }
}

export function maxLength(max: number) {
  return (value: string): string | null => {
    return value.length > max ? `最大${max}文字まで入力できます。` : null
  }
}
  • 検証関数はエラーがある場合はエラーメッセージ文字列を、有効な場合はnullを返します。
  • 範囲検証は高階関数(Higher-Order Function)で作成し、閾値を注入します。

15.2.3. カスタムルール

再利用可能な検証ロジックはComposableに分離します。

typescript
// src/composables/useFormValidation.ts
import { ref, type Ref } from 'vue'

type Validator = (value: string) => string | null

export function useFormValidation<T extends Record<string, string>>(
  form: Ref<T>,
  rules: Partial<Record<keyof T, Validator[]>>
) {
  const errors = ref<Partial<Record<keyof T, string>>>({})

  function validateField(field: keyof T) {
    const fieldRules = rules[field]
    if (!fieldRules) return

    for (const rule of fieldRules) {
      const error = rule(form.value[field])
      if (error) {
        errors.value[field] = error
        return
      }
    }
    delete errors.value[field]
  }

  function validateAll(): boolean {
    for (const field of Object.keys(rules) as (keyof T)[]) {
      validateField(field)
    }
    return Object.keys(errors.value).length === 0
  }

  return { errors, validateField, validateAll }
}

非同期検証(重複確認など)は別途の非同期検証関数で処理します。

typescript
async function checkDuplicateEmail(email: string): Promise<string | null> {
  const { isDuplicate } = await api.get(`/users/check-email?email=${email}`)
  return isDuplicate ? '既に使用されているメールアドレスです。' : null
}
  • 非同期検証は@blurのタイミングでのみ実行します。
  • ネットワークリクエストが含まれるため、デバウンスを適用することを推奨します。

15.2.4. エラーメッセージの表示

検証エラーは該当フィールドの下部にインラインで表示します。

vue
<script setup lang="ts">
import { ref } from 'vue'
import { useFormValidation } from '@/composables/useFormValidation'
import { required, email, minLength } from '@/utils/validators'

const form = ref({ email: '', password: '' })

const { errors, validateField, validateAll } = useFormValidation(form, {
  email: [required, email],
  password: [required, minLength(8)],
})
</script>

<template>
  <form @submit.prevent="validateAll() && handleSubmit()">
    <div :class="{ 'has-error': errors.email }">
      <input
        v-model="form.email"
        type="email"
        @blur="validateField('email')"
      />
      <p v-if="errors.email" class="error-message">{{ errors.email }}</p>
    </div>
    <div :class="{ 'has-error': errors.password }">
      <input
        v-model="form.password"
        type="password"
        @blur="validateField('password')"
      />
      <p v-if="errors.password" class="error-message">{{ errors.password }}</p>
    </div>
  </form>
</template>
  • エラーメッセージはv-ifで条件付きレンダリングします。
  • エラー状態のフィールドにはhas-errorクラスを追加して視覚的フィードバックを提供します。
  • エラーメッセージは日本語で作成し、ユーザーが理解できる具体的な案内を含めます。

TIENIPIA QUALIFIED STANDARD