16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Vue3 & Vuetify3でバリデーション付きのフォームを作ってみる

Posted at

この記事は、以前に投稿した「Vue & Vuetifyでバリデーション付きのフォームを作ってみる 」という記事の、Vue3.x 版となります。Vue2.xでの内容をご覧になりたい方は以前の記事をご覧ください。

VueVuetifyを組み合わせて、下図キャプチャのようなバリデーション付きのフォームを作ってみます。Vueの基本事項は知っている前提です。だいぶ回りくどい記事になっています。

Sample

なお、本記事では、Options APIではなく、Composition APIを使用しています。

バージョン

  • Vue 3.2.33
  • Vuetify 3.0.0 Beta 3

Vuetifyは、Vue3.xに対応したVuetify3.xがまだ正式リリースされていません。(2022/6/3現在)
ですので、この記事ではベータ版を使用しています。今後、仕様が変更となる可能性がありますのでご注意ください。

STEP0: ベース

デモ

See the Pen Vue3 & Vuetify3でのバリデーション付きフォーム STEP:0 by テクニカル座 (@tekunikaruza) on CodePen.

解説

本記事では、以下のコードをベースに、バリデーション付きのフォームを作っていきます。

HTML
<div id="app">
  <v-app>
    <v-card>
      <v-card-title>STEP:0 フォーム</v-card-title>
      <v-card-text>
        <v-text-field
          v-model="state.text1"
          label="入力必須で文字数制限のあるテキストフィールド"
        >
        </v-text-field>
        <v-text-field
          v-model="state.text2"
          label="入力必須のみあるテキストフィールド"
        >
        </v-text-field>
        <v-text-field
          v-model="state.text3"
          label="なんの制約もないテキストフィールド"
        >
        </v-text-field>
      </v-card-text>
      <v-divider></v-divider>
      <v-card-actions>
        <v-btn text v-on:click="submit">送信する</v-btn>
        <span v-if="state.success">送信成功!</span>
      </v-card-actions>
    </v-card>
  </v-app>
</div>
JavaScript
const app = Vue.createApp({
  setup() {
    // リアクティブデータ
    const state = Vue.reactive({
      // 各テキストボックスの値
      text1: '',
      text2: '',
      text3: '',
      // 送信が成功したかどうかのフラグ
      success: false,
    })
    
    // 送信を試みるメソッド(「送信する」がクリックされたときに呼ばれる)
    const submit = () => {
      state.success = true
    }
    
    return {
      state,
      submit,
    }
  },
})

app.use(Vuetify.createVuetify())
app.mount('#app')

Vuetifyを使用しているので、だいたいのコントロールはv-から始まる名前のタグになります。

入力必須や文字数制限のあるテキストボックスや、なんの制約もないテキストボックスも存在します。本来は、「送信する」をクリックすると、すべてのテキストボックスの入力検証をクリアしたときのみ、「送信成功!」と表示されてほしいのですが、今は、どんな状態でも「送信成功!」と表示されてしまします。

STEP 0

STEP1: 各テキストボックスに制約をつける

このSTEPでは、各テキストボックスに入力規則を設けます。これにより、各テキストボックスの値が規則にそぐわない場合は、エラー表示させることができます。

デモ

See the Pen Vue3 & Vuetify3でのバリデーション付きフォーム STEP:1 by テクニカル座 (@tekunikaruza) on CodePen.

解説

今回使用しているv-text-fieldを含め、Vuetifyのすべてのフォームコントロールには、rulesという属性を付与でき、反映させたいバリデーション用関数の配列を渡すことで、1つ以上の制約をつけることができます。

rules属性を付与する前に、あらかじめvue側で、バリデーション用関数を準備しておきます。ここでは、setup()内に、必須入力と文字数制限の規則を反映させるための関数(requiredValidation()limitLengthValidation())を用意しました。return時に、当該関数も含めています。

JavaScript
const app = Vue.createApp({
  setup() {
    // リアクティブデータ
    const state = Vue.reactive({
      // 各テキストボックスの値
      text1: '',
      text2: '',
      text3: '',
      // 送信が成功したかどうかのフラグ
      success: false,
    })
    
    // ↓***** 新規追加
    
    // バリデーション関数
    const requiredValidation = (value) => !!value || '必ず入力してください' // 入力必須の制約
    const limitLengthValidation = (value) => value.length <= 10 || '10文字以内で入力してください' // 文字数の制約
    
    // ↑***** 新規追加
    
    // 送信を試みるメソッド(「送信する」がクリックされたときに呼ばれる)
    const submit = () => {
      state.success = true
    }
    
    return {
      state,
      // ↓***** 新規追加
      requiredValidation,
      limitLengthValidation,
      // ↑***** 新規追加
      submit,
    }
  },
})

// ***** (後略) *****

それぞれの関数はどちらとも、引数として入力された文字列が渡され、制約に合致していればtrueを返し、合致していなければエラーメッセージの文字列を返す関数です。

||を使った書き方が見慣れない方は、下記ページが参考になります。
演算子 || の特殊な使用方法 [Java Script - 基本 - Tips]
基本的に入出力の形があっていれば、どんな関数の書き方でもいけるはず。。。

以上で、用意したバリデーション用関数を、各テキストボックスへ反映させます。最初に説明した、rules属性を付与することで、必要な規則を1個でも2個でも何個でも反映させることができます。

HTML
<!-- (前略) -->

        <!-- :rules属性を追加 -->
        <v-text-field
          v-model="state.text1"
          label="入力必須で文字数制限のあるテキストフィールド"
          :rules="[requiredValidation, limitLengthValidation]"
        >
        </v-text-field>

        <!-- :rules属性を追加 -->
        <v-text-field
          v-model="state.text2"
          label="入力必須のみあるテキストフィールド"
          :rules="[requiredValidation]"
        >
        </v-text-field>

        <!-- 特に変更なし -->
        <v-text-field
          v-model="state.text3"
          label="なんの制約もないテキストフィールド"
        >
        </v-text-field>

<!-- (後略) -->

rules属性の中で、さきほど作った関数を指定しています。(ですので、属性名の手前には、:コロンまたは、v-bind:をつける必要があります。詳しくはVueドキュメント参照。)

ご覧の通り、rules属性が受け付けるのは、配列です。1番目のテキストボックスには、requiredValidationと、limitLengthValidationの2つの規則を付与しています。2番目は、requiredValidationのみ。3番目は、特に規則が必要ないため、rules属性も付与していません。

実際に、キーボードで入力してみると、バリデーションが動いていることがわかります。

STEP 1

Tips: 文字数カウンタをつける

このままでも良いのですが、1番目のテキストボックスは、「10文字まで」という制限があるにもかかわらず、ユーザは、入力を始めた時点では、最大入力可能文字数が何文字かがわかりません。さきほど実装したバリデーション用関数が発動してエラーメッセージが出て初めて、最大文字数の制約を知ることになります。これはちょっと不親切です。

これを解決する手段として、v-text-fieldには、counterという属性を付与することができます。これを使うと、その名の通りテキストボックスにカウンタが表示され、あと何文字入力できるか、ひと目でわかるようになります。

HTML
<!-- (前略) -->
        <!-- counter属性を追加 -->
        <v-text-field
          v-model="state.text1"
          label="入力必須で文字数制限のあるテキストフィールド"
          :rules="[requiredValidation, limitLengthValidation]"
          counter="10"
        >
<!-- (後略) -->

STEP1 TIPS

注意点としては、rules属性を使わずに、counter属性だけを使ったとしても、入力文字数の制約自体はなく、実質何文字でも入力できる点です。文字数制限をかけたいときは、必ずrules属性も併せて使ってください。

STEP2: フォーム全体をバリデーションチェック

STEP1が終わった時点で、各テキストボックスには入力規則をつけることができました。しかし、この時点でも、不正値が入力されていたとしても、「送信する」をクリックすると、送信成功となってしまいます。

STEP2 前提

このSTEPでは、これを解決します。

デモ

See the Pen Vue3 & Vuetify3でのバリデーション付きフォーム STEP:2 by テクニカル座 (@tekunikaruza) on CodePen.

解説

考え方としては、すべてのテキストボックスのバリデーションが通過しているかを確認し、通過している場合のみ、その先の処理を進める、という流れになります。

まず、現時点では、各テキストボックスが個々でバリデーションチェックをしているだけになりますので、これらをv-formタグで囲って、1つのフォームであることを認識させます。このv-formに対しては、後ほどVue側からアクセスしますので、ref属性を使って名前をつけておきます。(ここではtestForm

HTML
<!-- (前略) -->
      <v-card-text>
        <!-- ↓ 新規追加 -->
        <v-form ref="testForm">
        <!-- ↑ 新規追加 -->
          <v-text-field
            v-model="state.text1"
            label="入力必須で文字数制限のあるテキストフィールド"
            :rules="[requiredValidation, limitLengthValidation]"
            counter="10"
          >
          </v-text-field>
          <v-text-field
            v-model="state.text2"
            label="入力必須のみあるテキストフィールド"
            :rules="[requiredValidation]"
          >
          </v-text-field>
          <v-text-field
            v-model="state.text3"
            label="なんの制約もないテキストフィールド"
          >
          </v-text-field>
        <!-- ↓ 新規追加 -->
        </v-form>
        <!-- ↑ 新規追加 -->
      </v-card-text>
<!-- (後略) -->

次に、Vue側では、「送信する」がクリックされたときに、v-form内のすべてのコントロールのバリデーションが通過しているかを確認する処理を追記します。

JavaScript
const app = Vue.createApp({
  setup() {
    // リアクティブデータ
    const state = Vue.reactive({
      // ***** (中略) *****
      // 送信が成功したかどうかのフラグ
      success: false,
    })
    
    // ***** (中略) *****

    // ↓***** 新規追加
    // フォームコンポーネントの取得
    const testForm = Vue.ref()
    // ↑***** 新規追加
    
    // ↓***** 修正
    // 送信を試みるメソッド(「送信する」がクリックされたときに呼ばれる)
    const submit = async () => {
      // 検証
      const validResult = await testForm.value.validate()
      if (validResult.valid) {
        state.success = true
      } else {
        state.success = false
      }
    }
    // ↑***** 修正
    
    return {
      state,
      requiredValidation,
      limitLengthValidation,
      // ↓***** 新規追加
      testForm,
      // ↑***** 新規追加
      submit,
    }
  },
})

// ***** (後略) *****

まず、testFormsetup()内で扱うために、const testForm = Vue.ref()でコンポーネントを取得しています。同時に、setup()のリターン内にtestFormを加えています。(Vue3の仕様上、リターンしないと、testFormの中身がundefinedになってしまいます。)

続いて、「送信する」がクリックされたときに呼ばれるsubmit()メソッドを修正します。
testForm.value.validate()と書いて、testFormの中身のvalidate()関数を呼び出します。この関数を呼ぶことで、v-formで囲まれた全コントロールのバリデーションチェックをしてくれます。この関数は非同期関数ですので、返り値を取得するためにawaitをつけています。(なので、submit()にはasyncをつけています。)
ここでは、返り値をvalidResultに格納しています。

validResultは、検証結果が格納されているオブジェクトになっており、validResult.validには、検証を通過したかどうかの真偽値が入っています。すべてのコントロールが検証を通過した場合はtrueですが、通過しなかったコントロールがあった場合はfalseとなり、同時に、該当コントロールをエラー表示にしてくれます。
ここでは、trueとなった場合のみ、先の処理(ここでは「送信成功!」と表示させる)を進めさせています。

なお、validResult.errorMessagesには、該当するエラーメッセージの配列が格納されていますので、すべてのエラーメッセージを決まった場所に表示させたい場合などに利用できます。

STEP 2

なお、v-formには、validate()関数を含む3つの関数があります。

関数名 動作
validate() バリデーションチェックを実施します。返り値の中身は上述の通り。
resetValidation() バリデーションチェックをリセットします。使いみちとしては、エラー表示を一旦すべて解除させたときなど。返り値はなし。
reset() すべての入力内容を初期値に戻します。また、バリデーションチェックもリセットされます。返り値はなし。

以上で、目標としていたフォームを作ることができました!

まとめ

rules属性とv-formを使えばOKということがわかりました。

16
12
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
16
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?