0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Vue 3(Composition API)+ Vuelidateでにおいて、配列の各要素に対してバリデーションを実装する

Posted at

こんばんは。

今日はVue 3(Composition API)+ Vuelidate において、配列の各要素に対してバリデーションを行う場合の実装例をご紹介します。

今回のポイントは以下の通りです。

  • 配列を持つ親コンポーネントと、
  • 配列の要素(オブジェクト)に対するフォームを扱う子コンポーネント

を分けることで、要素ごとにバリデーションを実行しやすくする構成です。

親コンポーネント

親コンポーネントの概要

  • 親コンポーネントで「配列のデータ」と「Vuelidate のバリデーションルール(rules)」を定義し、useVuelidateを呼び出します。
  • 各要素を描画するときに、:vuelidate="$v.items[index]"という形で要素ごとの Vuelidate 状態を子コンポーネントへ渡します。
ParentForm.vue
<template>
  <div>
    <h1>親コンポーネント</h1>

    <!-- 配列データ(items)をループして、子コンポーネントに渡す -->
    <div v-for="(item, index) in items" :key="index" style="margin-bottom: 16px;">
      <ItemForm
        :item="item"
        :vuelidate="$v.items[index]"
      />
    </div>

    <!-- バリデーション全体のチェックを行う例 -->
    <button @click="onSubmit">送信</button>
    <div v-if="submitErrorMessage" style="color: red;">
      {{ submitErrorMessage }}
    </div>
  </div>
</template>

<script>
import { ref, computed } from 'vue'
import useVuelidate from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import ItemForm from './ItemForm.vue'

export default {
  name: 'ParentForm',
  components: {
    ItemForm,
  },
  setup() {
    // バリデーション対象となるデータ
    const items = ref([
      { name: '', age: '' },
      { name: '', age: '' },
    ])

    // Vuelidate で使用するルール定義
    // 配列の各要素に対して $each でルールを定義できる
    const rules = computed(() => ({
      items: {
        $each: {
          name: { required },
          age: { required },
        },
      },
    }))

    // useVuelidate の呼び出し(第二引数は実際のデータ)
    const v$ = useVuelidate(rules, { items })

    const submitErrorMessage = ref('')

    // 送信時のバリデーション例
    const onSubmit = async () => {
      // 全体のバリデーションチェック
      const isValid = await v$.value.$validate()
      if (!isValid) {
        submitErrorMessage.value = '入力に不備があります。'
      } else {
        submitErrorMessage.value = ''
        // 送信処理 ...
        alert('送信成功!')
      }
    }

    return {
      items,
      v$,
      onSubmit,
      submitErrorMessage,
    }
  },
}
</script>

上記の例では、items配列に対して、$eachキーを使い、
nameageそれぞれにrequiredバリデーションを付与しています。

  • v$.value.items[index]には、該当インデックスの要素のVuelidate状態が格納されます。

子コンポーネント

子コンポーネントの概要

  • 親から受け取った「配列の要素(item)」と「要素ごとの Vuelidate インスタンス(vuelidate)」
  • 入力フォームとバリデーションエラー表示
  • 自分自身の内部ではuseVuelidateを呼ばず、親コンポーネントから既に生成されたインスタンスを受け取る形にする
ItemForm.vue
<template>
  <div>
    <h2>子コンポーネント (ItemForm)</h2>

    <div>
      <label>名前:</label>
      <input v-model="item.name" />
      <!-- name フィールドのバリデーションエラー表示例 -->
      <span v-if="vuelidate?.name?.$error" style="color: red;">
        ※名前は必須です
      </span>
    </div>

    <div>
      <label>年齢:</label>
      <input type="number" v-model="item.age" />
      <!-- age フィールドのバリデーションエラー表示例 -->
      <span v-if="vuelidate?.age?.$error" style="color: red;">
        ※年齢は必須です
      </span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ItemForm',
  props: {
    // 親から受け取る、配列の要素
    item: {
      type: Object,
      required: true,
    },
    // 親から受け取る、要素ごとの Vuelidate インスタンス
    vuelidate: {
      type: Object,
      required: true,
    },
  },
}
</script>
  • v-model="item.name"のように、直接親のデータを変更しています。
  • バリデーションエラーの表示にvuelidate?.name?.$errorを使用しています。
    • ?.はオプショナルチェーン演算子で、vuelidatevuelidate.nameが未定義の場合でもエラーにならないようにするためのものです。

配列要素の追加・削除を行う場合

もし配列要素の追加や削除を行う場合は、以下のように親コンポーネントでitemsを変更してあげれば動的にバリデーションが適用されます。

<template>
  <div>
    <button @click="addItem">要素を追加</button>

    <div v-for="(item, index) in items" :key="index">
      <!-- 要素を削除するボタン例 -->
      <button @click="removeItem(index)">削除</button>
      <ItemForm :item="item" :vuelidate="$v.items[index]" />
    </div>
  </div>
</template>

<script>
import { ref, computed } from 'vue'
import useVuelidate from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import ItemForm from './ItemForm.vue'

export default {
  components: { ItemForm },
  setup() {
    const items = ref([
      { name: '', age: '' },
    ])

    const rules = computed(() => ({
      items: {
        $each: {
          name: { required },
          age: { required },
        },
      },
    }))

    const v$ = useVuelidate(rules, { items })

    const addItem = () => {
      items.value.push({ name: '', age: '' })
    }

    const removeItem = (index) => {
      items.value.splice(index, 1)
    }

    return {
      items,
      v$,
      addItem,
      removeItem,
    }
  },
}
</script>

このようにすると、要素を追加するたびに配列に新しいオブジェクトが追加され、そのオブジェクトにもバリデーションが動的に適用されるようになります。

まとめ

  • 親コンポーネントで配列データ (items) とVuelidateのルール定義・インスタンス生成を行う。
  • 子コンポーネントでは親から渡された単一要素(item)と対応するVuelidateインスタンス(vuelidate)を使ってフォーム入力とバリデーションエラー表示を行う。
  • 追加・削除など動的に要素を操作しても、Vuelidate$eachを通じて自動的に管理してくれる。

以上の手順で、配列に対して要素ごとのバリデーションを行う構成が実装可能です。

ぜひ参考にしてみてください。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?