Vue.js: Vuelidateでフォームを検証する

[Vuelidate](https://vuelidate.netlify.com)はフォームの検証(validation)をするためのVueライブラリです。モデルにもとづいて、データをさまざまなプロパティで確かめられます。本稿はこのライブラリの基本的な使い方をご紹介します。

# \<input\>要素にv-modelを定める
まずは、Vuelidateは使わずに、`<input>`要素にモデルを定めて、データバインディングしてみましょう。ここでは、Vue.jsおよぴVuelidateなどのライブラリはCDNから読み込みます。

```html:&lt;head&gt;要素
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
```

`<body>`要素に書くHTMLの要素とJavaScriptコードはつぎのとおりです。`<input>`要素を`v-model`ディレクティブでデータバインディングしました(「[フォーム入力バインディング](https://jp.vuejs.org/v2/guide/forms.html)」参照)。入力したテキストはその下の`<div>`に示されます。`v-model`に添えた[`.trim`](https://jp.vuejs.org/v2/guide/forms.html#trim)はテキストの前後の余分なスペースを除く修飾子です。

```html:&lt;body&gt;要素
<div id="app">
	<input v-model.trim="text">
	<div>入力: {{text}}</div>
</div>
<script>
new Vue({
	el: '#app',
	data: {
		text: ''
	}
});
</script>
```

なお、つぎのような簡単なスタイルを与えます。

```html:&lt;style&gt;要素
body {
	font-family: Verdana, Geneva, "sans-serif";
}
input {
	border: 1px solid silver;
	border-radius: 4px;
	background: white;
	padding: 5px 10px;
	font-size: 1.2em;
}
```


# Vuelidateを設定する
つぎに、Vuelidateの基本的な設定を行いましょう。Vuelidateのライブラリは、つぎのようにふたつCDNから`<script>`要素に読み込みます。

```html:&lt;head&gt;要素
<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/vuelidate.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuelidate@0.7.4/dist/validators.min.js"></script>
```

Vuelidateにより使えるようになるのは、つぎのふたつの機能です。

- `validations`コンポーネントオプション: Vueコンポーネントに検証の中身を定めます。
- `$v`モデルオブジェクト: Vueのモデルと検証の状態をプロパティとしてもつモデルのオブジェクトです。

`validations`オプションプロパティはVueインスタンスのオプションオブジェクトに加えて、検証するデータに対して何を確かめるのかオブジェクトで定めます。プロパティが検証するデータ、値はバリデータ(検証の設定)を納めたオブジェクトです。[`Vue.use()`メソッド](https://jp.vuejs.org/v2/guide/plugins.html#プラグインの使用)でVuelidateを使い、用いるバリデータは`validators`から変数に取り出しておいてください。`required`は入力が必須ということです。

```js:&lt;script&gt;要素
Vue.use(window.vuelidate.default);
const {required} = window.validators;
new Vue({
	// [略]
	data: {
		// [略]
	},
	validations: {
		text: {
			required
		}
	}
});
```

`$v`モデルは検証の現在の状態をもつオブジェクトです(「[`$v values`](https://monterail.github.io/vuelidate/#sub-v-values)」参照)。状態を示すプロパティ一式を備え、`validations`オプションに確かめる項目が定められます。`v-model`ディレクティブには`$v`モデルから検証するデータを得て、その`$$model`ブロパティを与えてください。

`$v`モデルは以下の`<body>`要素のようにテキストとして参照すると、備えるプロパティやその変化が確かめられてわかりやすいでしょう。たとえば、フィールドにひとたびテキストを入力すれば、`$dirty`バリデータが`true`に変わります(図001)。

図001■入力により検証のプロパティの値が変わる
<img src="https://qiita-image-store.s3.amazonaws.com/0/128949/2ce352c9-f771-1eeb-7740-cc6d864b990e.png" width="40%"> <img src="https://qiita-image-store.s3.amazonaws.com/0/128949/534f2937-1329-ebda-c259-ace7722b5541.png" width="40%">

```html:&lt;body&gt;要素
<div id="app">
	<!-- <input v-model.trim="text"> -->
	<input v-model.trim="$v.text.$model">
	<div>入力: {{text}}</div>
	<pre>{{$v}}</pre>
</div>
```

- `$model`: 検証するもとのモデルへの参照。Vueモデルを直に参照したときと同じ値が得られます。つまり、`this.$v.value.$model`と`this.value`は同じ値になるということです。この値を書き替えればモデルは更新され、`$touch`メソッドが自動的に呼び出されます。フィールドにはじめてフォーカスしたとき、`$dirty`が`true`になります。


# クラスをバインディングする
ひとたび入力したテキストフィールドはスタイルを変えましょう。このようなときに使うのが「[HTML クラスのバインディング](https://jp.vuejs.org/v2/guide/class-and-style.html#HTML-クラスのバインディング)」です。`<style>`要素につぎのようなスタイルを加えます。

```html:&lt;style&gt;要素
.dirty {
	border-color: forestgreen;
	background: mintcream;
}
```

そして、`<input>`要素にクラスをバインディングします。クラスを決めるのは、バリデータの値に応じて適用するクラスをオブジェクトで返す新たなメソッド(`status()`)です(なお、`:class`は`v-bind:class`の[省略記法](https://jp.vuejs.org/v2/guide/syntax.html#v-bind-省略記法)です。)。

```html:&lt;body&gt;要素
<input v-model.trim="$v.text.$model" :class="status($v.text)">
```

```js:&lt;script&gt;要素
new Vue({
	// [略]
	validations: {
		// [略]
	},
	methods: {
		status(validation) {
			return {
				dirty: validation.$dirty
			}
		}
	}
});
```

これで、ひとたび入力したテキストフィールドにはクラスがバインディングされて、スタイルは動的に変わります(図002)。

#### 図002■入力したテキストフィールドのスタイルが変わる
![qiita_08_001_003.png](https://qiita-image-store.s3.amazonaws.com/0/128949/f42d2571-74cf-80db-40ac-24219bac8fe6.png)



# 必要な文字数を定める
さらに、テキストフィールドに入力しなければならない最低文字数も決めましょう。用いるバリデータは`minLength`です(「[Builtin validators](https://monterail.github.io/vuelidate/#sub-builtin-validators)」参照)。`validations`オプションには最低文字数を引数にして定めてください。

```js:&lt;script&gt;要素
const {required, minLength} = window.validators;
new Vue({
	// [略]
	validations: {
		text: {
			required,
			minLength: minLength(3)
		}
	},
	// [略]
});
```

入力した文字数が足りなければ、`$error`バリデータが`true`になります。その値に応じて、クラスをバインディングすればよいでしょう(図003)。

```html:&lt;style&gt;要素
.error {
	border-color: red;
	background: mistyrose;
}
```

#### 図003■入力文字数が足りないと別のスタイルになる
<img src="https://qiita-image-store.s3.amazonaws.com/0/128949/03806390-4588-c1df-427c-cfbff06df6c5.png" width="40%">

つぎのコード001に、HTML要素とJavaScriptコード、およびCSSの定めをまとめます。併せて、サンプル001をCodePenに公開しました。

#### コード001■Vuelidateでテキスト入力に応じてフィールドのスタイルを変える
```html:&lt;body&gt;要素
<div id="app">
	<input v-model.trim="$v.text.$model" :class="status($v.text)">
	<div>入力: {{text}}</div>
	<pre>{{$v}}</pre>
</div>
```

```js:&lt;script&gt;要素
Vue.use(window.vuelidate.default);
const {required, minLength} = window.validators;
new Vue({
	el: '#app',
	data: {
		text: ''
	},
	validations: {
		text: {
			required,
			minLength: minLength(3)
		}
	},
	methods: {
		status(validation) {
			return {
				dirty: validation.$dirty,
				error: validation.$error
			}
		}
	}
});
```

```html:&lt;style&gt;要素
body {
	font-family: Verdana, Geneva, "sans-serif";
}
input {
	border: 1px solid silver;
	border-radius: 4px;
	background: white;
	padding: 5px 10px;
	font-size: 1.2em;
}
.dirty {
	border-color: forestgreen;
	background: mintcream;
}
.error {
	border-color: red;
	background: mistyrose;
}
```

#### サンプル001■Vue.js: Vuelidate basic
<p data-height="265" data-theme-id="0" data-slug-hash="wxxOvE" data-default-tab="js,result" data-user="FumioNonaka" data-pen-title="Vue.js: Vuelidate basic" class="codepen">See the Pen <a href="https://codepen.io/FumioNonaka/pen/wxxOvE/">Vue.js: Vuelidate basic</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>