入力フォームを作成すると入力した値が要件にあっているか確認するためにバリデーションを行う必要があります。vue.jsにはさまざまなバリデーションのライブラリがありますが、今回はVuelidateを利用します。

Vuelidateの公式マニュアルを読んで少し複雑でわかりにくいなと感じた人にぜひ読んでもらいたい文書です。最初は複雑だと感じるかもしれませんが一つ一つが必要な設定なので理解すれば便利だなという機能ばかりです。

バリデーションとは

ユーザがフォームから入力した値が要件を満たしているかチェックを行うのがバリデーションです。

例えば入力したメールアドレスがXXX@XXXのようなメールアドレスの形式にあっているかや数字を入力して欲しい項目に文字ではなく数字が入力されているかなどのチェックを行うことです。

Vuelidateを使用する環境の構築

手元のパソコンでも簡単に動作確認できるようにVuelidate, vue.jsはcdnを使って読み込みます。フォームを作成するのにbootstrapも使っているのでこれもcdnを使います。


<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" >
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/validators.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/vuelidate.min.js"></script>
vuelidateではvalidators.min.jsとvuelidate.min.jsの2つを利用します。bootstrapは必須ではありません。

任意の場所にindex.htmlファイルを作成します。

入力フォームの設定

入力フォームはbootstrapを使って作成します。htmlのコードは下記の通りです。まだvue.jsの設定は何も行なっていない状態です。


<div id="app" class="container mt-5">
    <h2>入力フォームバリデーション</h2>
    <div class="row">
        <div class="col-sm-8">
            <form>
                <div class="form-group">
                    <label for="name">名前:</label>
                    <input type="name" class="form-control" id="name">
                </div>                    
                <div class="form-group">
                    <label for="age">年齢:</label>
                    <input type="age" class="form-control" id="age">
                </div>
                <div class="form-group">
                    <label for="email">メールアドレス:</label>
                    <input type="email" class="form-control" id="email">
                </div>
                <button type="submit" class="btn btn-info">送信</button>
            </form>
        </div>
    </div>
</div>

ブラウザで確認すると下記のフォームが表示されます。

boostratpで入力フォーム
boostratpで入力フォーム

vue.jsの初期設定

入力フォーム作成後、vue.jsの設定を行います。dataプロパティにname, age , emailを追加してv-modelで各input要素と結びつけます。

titleはvuelidateとは関係ありませんが、vue.jsの動作確認を行うために追加しています。そのためvuelidateへの影響はありません。h1タグを<h1>{{ title }}</h1>に変更を行なっています。

const app = new Vue({
    el: '#app',
    data: {
        title: '入力フォームバリデーション',
        name: '',
        age: '',
        email: '',
    },
    methods: {
        submitForm(){
            console.log('submit');
        }
    }
});

html側は下記のように更新を行います。


<div class="row">
    <div class="col-sm-8">
        <form @submit.prevent="submitForm">
            <div class="form-group">
                <label for="name">名前:</label>
                <input type="name" class="form-control" id="name" v-model="name">
            </div>                    
            <div class="form-group">
                <label for="age">年齢:</label>
                <input type="age" class="form-control" id="age" v-model="age">
            </div>
            <div class="form-group">
                <label for="email">メールアドレス:</label>
                <input type="email" class="form-control" id="email" v-model="email">
            </div>
            <button type="submit" class="btn btn-info">送信</button>
        </form>
    </div>
</div>

ブラウザに表示される画面には先ほどと違いがありませんが、送信ボタンを押すとデベロッパーツールにsubmitが表示されることと下記のようにdataプロパティの中身を確認しておきます。

dataプロパティの中身をデベロッパーツールで確認
dataプロパティの中身をデベロッパーツールで確認
上記の内容を表示させるためには、Chromeの拡張機能のvue devtoolのインストールも必要になります。

Vuelidateの設定

Vuelidateの初期設定

vuelidateの初期設定については公式のドキュメントを参考に行います。

npmを使った場合とcdnを使った場合では方法は異なります。本文書ではcdnを使った場合のみ記述しています。

vuelidateライブラリをvue.jsのプラグインとして利用するためにVue.useをつかてグローバルに読み込みます。


Vue.use(window.vuelidate.default);
const {required, email} = window.validators;

validatatorsからバリデーションに利用したいバリデーターrequired, emailを読み込んでいます。requiredは入力が必須な項目をチェッックするバリデーターです。requiredの他にメールのチェックに使うemailや最小の長さのチェックに使うminValueなどさまざまなものが事前に準備されています。利用するバリデーターを増やしたい場合は、{required, email }の中に追加していくことで利用できるバリデーターを増やすことができます。

{ }の中にないバリデーターを利用するとエラーが発生します。

Vuelidateで事前に準備されているバリデーターについは公式ドキュメントに公開されています。

事前準備されているバリデーター
事前準備されているバリデーター

Vuelidate設定による動作の確認

Vuelidateを設定することによってどのようにバリデーションに関する項目に影響があるのか確認していきます。

vue.jsにvalidationsオプションを追加します。nameに関してのみバリデーターrequiredの設定を行います。


el: '#app',
data: {
    title: '入力フォームバリデーション',
    name: '',
    age: '',
    email: '',
},
validations: {
    name: {
        required
    }
},

もし、const {required, email} = window.validators;の設定を行わない場合は下記のように記述することでバリデーションのrequiredを利用することは可能です。


validations: {
    name: {
        required: validators.required
    }
},

設定が完了したら、デベロッパーツールを開いてください。validationsオプションをvue.jsに追加したことで新たに$vオブジェクトが表示されます。$vオブジェクトの中にrequiredの設定を行なったnameを確認することができます。

$vオブジェクの確認
$vオブジェクの確認

nameオブジェクト部分のみ拡大して確認します。色々なプロパティが入っていますがその中でもmodel, $invalid, requiredの3つに注目します。

nameの拡大
nameの拡大

これらの値はブラウザ上でフォームに入力を行うと変化するので、ブラウザ上の入力フォームに名前を入力します。

入力フォームに入力後のnameオブジェクト
入力フォームに入力後のnameオブジェクト

注目していた3つの値を確認すると$modelにはブラウザ上で入力したJohn Doeが入り、requiredはfalseからtrueに$invalidはtrueからfalseに変化していることがわかります。

requiredは必須入力のバリデーターを表しているので入力することでfalseからtrueに変更になることがわかります。$invalidがtureからfalseに変更になることでnameに関するバリデーション(ここではrequired)をクリアしたことを表しています。

これらの値を確認することでユーザへのエラーメッセージの表示等の制御を行うことができます。

$invalidの確認方法

デベロッパーツールの中で$invalidの値を確認することができましたが、JavaScriptのコードでどのように$invalidの値を取得するか確認します。

送信ボタンをクリック後に実行されるsubmitFormメソッドにconsole.logを追加します。


methods: {
    submitForm(){

        console.log(this.$v.name.$invalid)

        console.log('submit');

    }
}

入力フォームに何も入力していない状態で送信ボタンを押すとコンソールには、tureとsubmitが表示されます。何か名前を入力して送信ボタンを押すととfalseとsubmitが表示されることを確認することができます。JavaScript上では、this.$v.name.$invalidの値に$invalidが入っていることがわかります。

$v.name.$invalidではnameに関するバリデーションのみ確認を行うことができますがnameを含めたすべての項目のバリデーションの確認には$v.$invalidを使うことができます。入力フォームの1つでのバリデーションをクリアしていない場合はエラーが表示されるように変更しておきます。バリデーションを通過した場合は、データベースへの登録などに関する処理を追加します。


methods: {
    submitForm(){

        if(this.$v.$invalid){

            console.log('バリデーションエラー');

        }else{

            // データ登録の処理をここに記述
            console.log('submit');

        }
    }
}

ユーザへのメッセージの表示

HTML上では、$v.name.requiredの値をチェックすることで名前の項目に入力が行われたどうか確認することができます。この値を利用して、ブラウザ上にメッセージを表示させることができます。

v-ifディレクティブと$v.name.requiredの値を利用します。名前に何も入力していない場合は、$v.name.requiredの値はfalseなので!false=trueになり、メッセージが表示されます。入力を行うと値はtrueになるので!true = falseにより、メッセージは非表示になります。


<div class="form-group">
    <label for="name">名前:</label>
    <input type="name" class="form-control" id="name" v-model="name">
    <span v-if="!$v.name.required">名前が入力されていません。</span>
</div>

入力前だとメッセージが表示されています。

入力前なのでメッセージが表示
入力前なのでメッセージが表示

入力を行うとメッセージが非表示になります。

入力するとメッセージが消える
入力するとメッセージが消える

送信ボタンの制御

バリデーションにエラーがある場合は、送信ボタンを押せない状態にすることも可能です。


<button :disabled="$v.$invalid" type="submit" class="btn btn-info">送信</button>

ボタン自体は表示されていますが、バリデーションを通過するまではボタンを押すことはできません。

エラーメッセージ表示のタイミング

ここまでの設定では、ブラウザで入力フォームにアクセスすると何も入力していないのにエラーメッセージが表示されています。

入力前なのでメッセージが表示
何もしていないのにメッセージが表示

アクセス直後からメッセージが表示されているのではなく入力後にメッセージが表示されるように変更を行います。

デベロッパーツールのnameオブジェクトの中にあった$dirtyプロパティを利用します。

nameの拡大
nameの拡大

$dirtyプロパティは、nameに何か入力が行われると$dirtyの値がfalseからtrueに変更になります。しかし、$invalidとは異なり、この値は自動では変更されません。この値を変更するためには$v.name.touch()が必要になります。inputイベントを利用して$v.name.touch()を実行し、$dirtyの値を変更します。


<input type="name" class="form-control" id="name" v-model="name" @input="$v.name.$touch()">

$v.name.$touch()設定後に入力を行うと文字を入れた直後に$dirtyの値がfalseからtrueになります。

$dirtyの値が変わる
$dirtyの値が変わる
dirtyという単語には汚れという意味があるので、データを入力することによって$dirtyがfalseのクリーンの状態から汚れた状態であるtrueへの変化を表していると考えることができます。

もう一つnameオブジェクトには重要なプロパティ$errorがあります。$errorの値は、$invalidと$dirtyの2つの値の組み合わせで、どちらかがfalseであればfalseでどちらもtrueであればtrueの値をとります。

$errorプロパティを使うことでブラウザでアクセス直後にはメッセージを表示させない制御を行うことが可能になります。


<input type="name" class="form-control" id="name" v-model="name" @input="$v.name.$touch()">
<span v-if="$v.name.$error">名前が入力されていません。</span>
アクセス直後は$dirtyはfalseで$invalidはtrueなので$errorはfalseのためメッセージは表示されません。入力後は$dirtyがtureで$invalidがfalseになり$errorがfalseになる必要があります。
入力前はメッセージは表示されない
入力前はメッセージは表示されない

エラーメッセージ表示のタイミング(2)

nameのみを利用してきましたが、次はemailを使ってバリデーションを行なっていきます。

validationsオプションにemailのバリデーションを追加します。バリデーターにはrequired, emailを設定します。


validations: {
    name: {
        required
    },
    email: {
        required,
        email
    }
},

バリデーターを2つ設定したので、バリデーターごとに異なるメッセージが表示させるようにメッセージは2つに分けます。アクセス直後にメッセージが表示されないように$v.email.$touch()と$v.email.$errorを使います。


<div class="form-group">
    <label for="email">メールアドレス:</label>
    <input type="email" class="form-control" id="email" v-model="email" @input="$v.email.$touch()">
    <div v-if="$v.email.$error">
        <span v-if="!$v.email.required">メールドレスが入力されていません。</span>
        <span v-if="!$v.email.email">メールアドレスの形式が正しくありません。</span>
    </div>
</div>
入力前はメッセージは表示されない
入力前はメッセージは表示されない

しかし、メールアドレスに入力した瞬間にエラーメッセージが表示されます。

jを入れた瞬間にエラーメッセージが表示
jを入れた瞬間にエラーメッセージが表示

入力した瞬間ではなく入力が完了して、カーソルを外した際にメッセージが表示されるように変更を行います。@inputではなく@blurイベントに変更します。


<input type="email" class="form-control" id="email" v-model="email" @blur="$v.email.$touch()">
カーソルを外すとメッセージが表示
カーソルを外すとメッセージが表示
john@example.comなど正しいメールアドレスを入力した場合はエラーメッセージは表示されません。

classの設定

バリーデーションがエラーの入力項目については、class属性のバインドを利用することでスタイルを変更することでユーザがどの項目がエラーになっているかを簡単に識別できるようにします。

$v.name.$errorがtrueの場合はクラスのerrorが適用され、falseの場合は適用されません。


<input type="email" class="form-control" id="email" v-model="email" @blur="$v.email.$touch()" :class="{ error : $v.email.$error,'form-control': true }">

errorクラスは下記のスタイルを適用します。


<style>
    .error {
	color: #8a0421;
	border-color: #dd0f3b;
	background-color: #ffd9d9;
}
</style>

メールアドレスの形式が正しくない場合は、カーソルをinput要素から外すと背景の色が変わります。

バリデーションがエラーの場合はクラスを適用
バリデーションがエラーの場合はクラスを適用

送信ボタンの変更

送信ボタンについては:disabledと$v.invalidを利用して、バリデーションにエラーがある場合はクリックを行うことができないように制御しました。

送信ボタンの無効化だとわかりにくい場合があるので、送信ボタンを押してバリデーションにエラーがある場合は、各inputの下にエラーメッセージが表示されるように変更します。

ボタンから:disableを削除します。


<button type="submit" class="btn btn-info">送信</button>

submitFormメソッドにthis.$v.$touch()は追加します。追加することで送信ボタンを押すとすべての入力項目の$dirtyがtrueになります。


methods: {
    submitForm(){

        this.$v.$touch();

        if(this.$v.$invalid){

            console.log('バリデーションエラー');

        }else{

            // データ登録の処理をここに記述
            console.log('submit');

        }
    }
}

実際にブラウザでアクセス後に送信ボタンを押すとエラーメッセージが表示されます。

送信ボタンを押すとエラーメッセージが表示
送信ボタンを押すとエラーメッセージが表示
ageについてはバリデーションを設定していないのでエラーメッセージは表示されません。

まとめ

ここまでの読み終えるとVuelidateの基本的な使用方法は理解できたかと思います。今回作成したコードは下記となります。index.htmlを作成し、コピー&ペーストするだけでVuelidateの動作確認を行うことができます。

下記のコードではname, emailだけではなくageにもバリデーションを追加しています。バリデーターにはrequired, integerを設定しています。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" >
<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/validators.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/vuelidate.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<title>Vuelidate</title>
<style>
    .error {
	color: #8a0421;
	border-color: #dd0f3b;
	background-color: #ffd9d9;
}
</style>
</head>
<body>
    <div id="app" class="container mt-5">
    <h2>{{ title }}</h2>
    <div class="row">
        <div class="col-sm-8">
            <form @submit.prevent="submitForm">
                <div class="form-group">
                    <label for="name">名前:</label>
                    <input type="name" id="name" v-model="name" @blur="$v.name.$touch()" :class="{ error : $v.name.$error,'form-control': true }">
                    <span v-if="$v.name.$error">名前が入力されていません。</span>
                </div>                    
                <div class="form-group">
                    <label for="age">年齢:</label>
                    <input type="age" class="form-control" id="age" v-model="age" @blur="$v.age.$touch()" :class="{ error : $v.age.$error,'form-control': true }">
                    <div v-if="$v.age.$error">
                            <span v-if="!$v.age.required">年齢が入力されていません。</span>
                            <span v-if="!$v.age.integer">整数の数字以外は入力できません。</span>
                        </div>
                </div>
                <div class="form-group">
                    <label for="email">メールアドレス:</label>
                    <input type="email" class="form-control" id="email" v-model="email" @blur="$v.email.$touch()" :class="{ error : $v.email.$error,'form-control': true }">
                    <div v-if="$v.email.$error">
                        <span v-if="!$v.email.required">メールドレスが入力されていません。</span>
                        <span v-if="!$v.email.email">メールアドレスの形式が正しくありません。</span>
                    </div>
                </div>
                <button type="submit" class="btn btn-info">送信</button>
            </form>
        </div>
    </div>

    </div>
<script>

Vue.use(window.vuelidate.default);
const {required,email,integer} = window.validators;

const app = new Vue({
    el: '#app',
    data: {
        title: '入力フォームバリデーション',
        name: '',
        age: '',
        email: '',
    },
validations: {
    name: {
        required
    },
    email: {
        required,
        email
    },
    age: {
        required,
        integer
    }
},
methods: {
    submitForm(){

        this.$v.$touch();

        if(this.$v.$invalid){

            console.log('バリデーションエラー');

        }else{

            // データ登録の処理をここに記述
            console.log('submit');

        }
    }
}
});

</script>    
</body>
</html>