2年前に書いた HTML5 Form Validationサンプル(jQuery + Bootstrap 4.0 + Constraint Validation API) の Vue版です。
HTML5のフォーム要素に備わっているValidation機能(Constraint Validation API)を利用するフォーム要素のコンポーネントと、それを利用したフォームのサンプルを作ってみた。
Form Validation Library
カスタムバリデーションについては公式のドキュメントに説明があります。
著名なライブラリを使う手もあります。
サンプルコード
概要
スタイルはBulmaです。
components/email.vue でメールアドレス用のコンポーネントを作ってあります。
APIによるバリデーション
フォーム要素はApp.vueにあります。
このフォーム要素にはnovalidate属性でブラウザのエラーメッセージ表示をオフにしてあります。
<form class="form" ref="form" @submit.prevent="onSubmit" novalidate> <email-input v-model="email"></email-input> <button type="submit" class="button is-primary">Submit</button> </form>
このままだとエラーがあっても通ってしまうので、submitイベントハンドラ内で checkValidity を見ます。
methods: {
async onSubmit() {
if (!this.$refs.form.checkValidity()) {
return await false;
}
try {
await fetch('https://jsonplaceholder.typicode.com/user/1')
alert('submit!');
this.email = null;
} catch (e) {
alert('error!');
}
}
}
checkValidity はエラーがあると false になるので、 先頭でreturnすれば送信を停止できる。
コンポーネントで v-model を使う
email.vueはコンポーネントとしてApp.vueに登録してあります。
<email-input v-model="email"></email-input>
入力された値を送信時に使うので、App.vueのdata()にあるemailをmodelとして使いたいです。
コンポーネントで v-model を使う方法は公式ドキュメントに説明があるので、この通り実装します。
input要素に v-bind:value と v-on:input を追加する。
<input type="email" id="input-email" name="email"
class="input" :class="stateClass"
ref="input" :value="value"
@input="onInput"
@invalid="showFeedback"
:placeholder="placeholder" required>
props に value を追加。
props: {
value: {
type: String,
default: null
},
}
v-on:input に $emit('input', $event.target.value) を設定する。
サンプルはついでにバリデーションもするので、onInputイベントハンドラを利用してます。
onInput(e) {
this.showFeedback(e);
this.$emit('input', e.target.value);
},
ValidityStateの確認
フォーム要素にはvalidityというプロパティにValidityStateオブジェクトがあります。
これにバリデーションの結果がbooleanで納めてあり、trueだとエラーという意味です。
{
//ユーザーがUIが値に変換できない入力をした場合に true
badInput: '',
//要素の値が与えられたパターンにマッチしない場合に true
//[pattern]
patternMismatch: '',
//要素の値が与えられた最大値を超える場合に true
//[max]
rangeOverflow: '',
//要素の値が与えられた最小値を下回る場合に true
//[min]
rangeUnderflow: '',
//要素の値が step 属性で与えられた規則に合わない場合に true
//[step]
stepMismatch: '',
//要素の値が与えられた長さより長い場合に true
//[maxlength]
tooLong: '',
//要素の値が与えられた長さより短い場合に true
//[minlength]
tooShort: '',
//要素の値が正しい構文ではない場合に true
//[type=url][type=email]
typeMismatch: '',
//要素が入力必須のフィールドであるのに値がない場合に true
//[required]
valueMissing : ''
}
全部チェックしてtrueがあれば、そのkeyに対応するメッセージを表示すればいいですが、
ValidityStateにはObject.entriesやObject.keysなどObjectメソッドは利用できないのでfor-inを使います。
for(const key in e.target.validity){
if (e.target.validity[key]) {
this.invalidFeedback = this.feedbackMessage[key];
this.state = false;
}
};
サンプルのemail要素に対してAPIが吐くエラーは valueMissing(requiredの未入力) と、 typeMismatch(書式ミス) です。
これに対するメッセージをdataで持っておけば、エラーがあった場合にkeyを使って得られます。
feedbackMessage: {
valueMissing: 'This field is required',
typeMismatch: 'This email is invalid'
},
バリデーションエラーがなかった場合は、validity.valid が trueなので、
for-inでエラーをチェックする前にvalidのチェックをします。
これを忘れるとエラーがないのにエラーになります…。
リセット
送信後にフォームの内容をリセットするのは、v-modelにnullを設定するだけです。
this.email = null;
form要素のreset()はinput要素の内容は空になるものの、v-model変数にnullを追加するわけではないので内部では値が残ったままになります。
email.vueではwatchでvalueを監視して、nullだった場合にstateとエラーメッセージをクリアします。
watch: {
value(val, oldVal) {
if (val === null) {
this.state = null;
this.invalidFeedback = null;
}
}
},