やりたいこと
一覧形式のフォームで、全データのバリデーション後に送信
この記事では、Vuetifyのv-data-tableを使って、一覧形式で表示されたユーザーの入力欄を持つフォームでデータ送信前に一括でバリデーションを行う方法を記載します。
今回は各ユーザーの「項目A(下限・上限)」と「項目B(下限・上限)」の各入力欄で「下限値が上限値より大きい」という矛盾が発生していないかをチェックし全ての入力のエラーが一切ない場合にのみデータを送信します。
一括バリデーションのメリット
- データの整合性: 送信前にまとめてエラーをチェックするので、サーバーに送るデータの整合性を保証できる。
- ユーザーの負担軽減: ユーザーはまとめてエラーを確認・修正できる。
- サーバー負荷の軽減: 不正なデータを含むリクエストを減らせるので、サーバーの負荷を減らせる。
実装のポイント:メソッドでバリデーションチェック
各入力欄の値(下限値と上限値)を取得し「下限値が上限値より大きい」というエラーがないか確認します。
エラーが見つかった場合は、エラーチェック用のオブジェクトに記録します。
コード全体
<template>
<v-data-table
:items="localItems"
>
// 入力欄
<template v-slot:item.itemA="{ item }">
<div>
<v-text-field
v-model.number="item.itemA.value1"
:rules="itemRulesA[item.id] || []"
@input="checkInput(item, 'itemA')"
/>
<div>〜</div>
<v-text-field
v-model.number="item.itemA.value2"
:rules="itemRulesA[item.id] || []"
@input="checkInput(item, 'itemA')"
/>
</div>
</template>
<template v-slot:item.itemB="{ item }">
<div>
<v-text-field
v-model.number="item.itemB.value1"
:rules="itemRulesB[item.id] || []"
@input="checkInput(item, 'itemB')"
/>
<div>〜</div>
<v-text-field
v-model.number="item.itemB.value2"
:rules="itemRulesB[item.id] || []"
@input="checkInput(item, 'itemB')"
/>
</div>
</template>
</v-data-table>
</template>
-------------------
<script>
export default {
data() {
return {
errorsObject: {}, // エラー情報を記録するオブジェクト
localItems: [], // データテーブルのアイテム
itemRulesA: {}, // 項目Aのルール(エラーメッセージ表示用)
itemRulesB: {}, // 項目Bのルール(エラーメッセージ表示用)
};
},
methods: {
/**
* 項目A・項目Bが[下限 > 上限]の場合のバリデーションチェック
* @param {object} item - 各ユーザーのデータ
* @param {string} itemHeadName - 入力した項目名('itemA' or 'itemB')
*/
checkInput(item: any, itemHeadName: string) {
const lowerValue = Number(item[itemHeadName].value1); // 下限値
const upperValue = Number(item[itemHeadName].value2); // 上限値
// エラーオブジェクトの初期化(必要時のみ)
this.errorsObject[item.id] = this.errorsObject[item.id] || {};
const error = lowerValue > upperValue;
// エラーの有無とエラーメッセージを設定
this.errorsObject[item.id][itemHeadName] = error;
if (itemHeadName === 'itemB') {
this.itemRulesB[item.id] = error
? ['ルール:下限が上限を超えてはいけません']
: [];
} else if (itemHeadName === 'itemA') {
this.itemRulesA[item.id] = error
? ['ルール:下限が上限を超えてはいけません']
: [];
}
// 全てのエラーをチェックして問題なければ送信
if (!this.checkErrors()) {
this.$emit('itemInput', this.localItems);
}
},
/**
* 全てのユーザーの入力データにエラーが存在するかどうかを判定する
* @returns {boolean} エラーが存在する場合はtrue、そうでない場合はfalse
*/
checkErrors() {
return Object.values(this.errorsObject).some(rowErrors =>
Object.values(rowErrors).some(error => error)
);
},
}
};
</script>
コード解説
template:
v-data-table:localItemsのデータを表示します。v-slot:item.itemA, v-slot:item.itemB:「項目A」と「項目B」の列の表示内容を定義v-text-field: テキストフィールドコンポーネント- v-model.number:number修飾子で数値として扱い、ユーザーの入力値をlocalItems内のデータと紐づけ。
- @input:項目に入力があるたびにcheckInputメソッドを呼び出す。
- :rules:エラーメッセージを表示するためのルールを設定します。
itemRulesAとitemRulesBからエラーがあればエラーメッセージを返します。
script:
-
data:
errorsObject:エラー情報を記録するオブジェクト。ユーザーIDと項目名をキーにして、エラーがあるかどうか (true or false)を記録localItems:データテーブルに表示するユーザーデータ。itemRulesA,:エラーメッセージ表示用のルールを格納するオブジェクト。itemRulesB
ユーザーIDをキーにエラーメッセージを返す関数を配列を保持
-
methods:
-
checkInput:
1. 入力された「下限値」と「上限値」を取得
2. エラー情報を記録するerrorsObjectを、必要に応じて初期化
3. 「下限値 > 上限値」ならエラーの判別でerrorsObjectにtrueを記録
4. エラーがあればitemRulesA または itemRulesBにエラーメッセージ表示用の関数を追加
5. checkErrorsメソッドを呼び出し、全てのエラーをチェックして問題なければ親コンポーネントに送信 -
checkErrors:
1. errorsObjectをチェックして、エラーが記録されているかどうかを判定
2. エラーがあればtrueを返し、なければfalseを返す。
-
一覧フォームの入力バリデーションのポイント
- エラー情報をerrorsObjectに記録:エラーがあったら場合、errorsObjectに記録します。
// errorsObject の構造 { "user1のID": { "itemA": false, // 項目Aはエラー:なし "itemB": true // 項目Bはエラー:あり }, "user2のID": { "itemA": false, // 項目Aはエラー:なし "itemB": false // 項目Bはエラー:なし } } - 全てのエラーをチェックして問題なければ送信:checkErrorsでエラーがないことを確認してから親コンポーネントにデータを送信します。
まとめ
この記事では一覧形式のフォームでバリデーションを行う方法 を解説いたしました。
この方法なら、複数の入力欄でも間違ったデータを送信することなくすべての入力欄をまとめて修正できるため、ユーザーフレンドリー且つデータの整合性も保証できると思います。ぜひ活用してみてください!