入力値チェック(バリデーション)
- FormControllerでできること
- 基本的な使い方
- (1) 入力値の取得と集約(オブジェクト化)
- (2) 入力値のバリデーション
- (3) バリデーション結果(エラー)の使用と画面出力
- バリデーション結果オブジェクト(ValidationResult)の内容
- プラグインが実装する表示更新タイミング関数(実装していない関数は呼ばない)
- コードイメージ
- メモ
FormControllerでできること
FormControllerは、HTMLのフォームに関する機能を集約したコントローラです。
大きく下記3つの機能を持ちます。
- フォームの入力値の管理(値の取得と集約・オブジェクト化)
- 入力値バリデーション
- バリデーション結果の画面への反映
なお、結果の画面への反映については、FormControllerに対して「出力プラグイン」をセットすることで実現します。
各種出力プラグインを通してバリデーションエラーを画面に表示したり入力要素のスタイルを変更したりすることができます。
詳細:FormControllerのJSDoc、FormValidationLogicのJSDoc
バリデーションの流れ
フォームのバリデーションは、下記のように3つのフェーズに分けて実行されます。
- 入力値の取得とオブジェクト化
- 各入力値を保持したオブジェクトに対するバリデーション
- バリデーション結果の表示(具体的な表示は出力プラグインを通して行う)
上記の処理は、FormController.validate() を呼び出す、
または設定によって入力値が変更された場合・キー入力があった場合など
特定のイベントが発生したタイミングで行われます。
基本的な使い方
FormControllerの典型的な利用例では、下記のようなセットアップを行います。
- FormControllerをform要素にバインド
- 「バリデーションルール」を設定
- 出力プラグインを設定
以降、各フェーズごとに機能・設定方法を説明します。
特に、バリデーションルールの設定については(2)を参照してください。
(1) 入力値の取得と集約(オブジェクト化)
バリデーションの最初のフェーズでは、フォームに属する入力値を取得し、オブジェクト化します。
この処理はFormController.getValue()と同等です。
getValue()メソッドを呼び出すと、バリデーションは行わず、このコントローラで管理されている全ての入力要素(input, select, textarea)の入力値を集約し、オブジェクトとして取得できます。
オブジェクトのキーは入力要素のname属性の値、値は入力値です。
また、getValue()の引数に特定のname属性の値(複数可)を渡すと、
そのname属性の入力要素の値のみを含んだオブジェクトが返ります。
入力値のグループ化
入力値をオブジェクトにまとめる際、同じ「グループ」に属する入力要素の値を
ネストしたオブジェクトとしてまとめることができます。
グループは、FormControllerをバインドしている要素(formタグ)の中にある、
複数の入力要素を囲む親要素に「data-h5-input-group-container」属性を付与することで作成できます。
この属性の値は入力要素のname属性と同様値を集約するときのキーとして使われるので、
他の入力要素のname属性と重複しない値にする必要があります。
例:
<div data-h5-input-group-container="birthday">
<input name="year" type="text">
<input name="month" type="text">
<div> <!-- このように、コンテナとinputは直接の親子でなくてもよい -->
<input name="date" type="text">
</div>
</div>
</form>
上記のようなHTMLを記述し、id="myForm"のフォームにFormControllerをバインドします。
この状態でformController.getValue()を呼ぶと、下記のようなオブジェクト(値は例)が返ります。
year: '2019',
month: '2',
date: '18',
birthday: {
year: '2019',
month: '2',
date: '18'
}
};
上記の例の通り、<div data-h5-input-group-container="birthday"></div> の中にある入力要素の値が、
birthday: { year: , month: , date: } というオブジェクトにまとめられていることがわかります。
また、例の通り、getValue()の戻り値のオブジェクトの第一階層には、グループ化していないyear, month, dateの個別の値も含まれます。
(2) 入力値のバリデーション
入力値の取得が完了すると、次に、値のバリデーションを行います。
バリデーションは、FormControllerにセットされた「バリデーションルール」の設定に基づいて行われます。
また、フォームコントローラをform要素にバインドした場合、機能の競合を避けるため、HTML5仕様によるブラウザ標準のバリデーションとのは無効化されます。
(form要素に自動的にnovalidate属性が付与されます。)
バリデーション動作の基本
FormControllerのバリデーションは
- 設定されたバリデーションルールに基づき、FormControllerがユーザー操作(イベント)を検知して自動的に実行
- validate()メソッドの呼び出しによる手動実行
の2つの方法で実行することができます。
また、FormControllerは「非同期バリデータ」の仕組みをサポートしており、
入力値の有効性をサーバに問い合わせてチェックしたい場合などにこの仕組みを利用することで
画面更新などを統一的に実装することができます。
バリデーション状態について
FormControllerによるバリデーションでは、各入力要素は
- 未バリデーション状態(一度もバリデーションを行っていない(一つ以上のバリデータが適用されていない)状態)
- バリデーション済み状態(バリデーションした結果エラーがないことが確認された状態)
- バリデーションエラー状態
- 非同期バリデーション結果待ち状態
のいずれかに分類されます。
バリデーション実行後、
- バリデーションを行ったすべての要素(name)はValidationResult.propertiesに格納されます。
- 「バリデーション済み状態」の要素(name)はValidationResult.validPropertiesに格納されます。
- 1つ以上のルールに違反した、「バリデーションエラー状態」の要素(name)は ValidationResult.invalidPropertiesに格納されます。
- どのルールに違反したのかを個別に知りたい場合はValidationResult.invalidReasonで参照できます。
- 「非同期バリデーション結果待ち状態」の要素(name)はValidationResult.validatingPropertiesに格納されます。
なお、未バリデーション状態の要素はいずれにも含まれません。
ユーザー操作によるバリデーションの自動実行
バリデーションの自動実行は、入力要素で下記いずれかのイベントが発生したタイミングで行うことができます。
- 入力要素にフォーカスがあたったとき(FOCUS)
- 入力要素からフォーカスが外れたとき(BLUR)
- 内容が変更された(要素のchangeイベントが発生した)とき(CHANGE)
- キー入力した(要素のkeyupイベントが発生した)とき(KEY_UP)
各タイミングは h5.validation.ValidationTiming 列挙体で定義されています。
バリデーションルールの設定
JavaScriptコードでの設定方法
HTMLに記述する設定方法
バリデーションの実行タイミングとその制御
バリデーションは、フォームの送信ボタンを押した時(submitイベントが発生したとき)のほか、
フォームに紐づく入力要素で下記のユーザー入力イベント(VALIDATE以外)が発生した場合に実行されます。
送信ボタンが押されたときはフォームに紐づく全入力要素が、
ユーザー入力イベントが発生したときはその入力要素のみがバリデーション対象になります。
(FormController.validate()を呼んだ場合、引数を省略した場合は全入力要素が、
引数にバリデーションを行うプロパティを渡した場合はその要素のみが対象になります。)
※イベントが発生した要素が入力グループの一部の場合は、その項目単体としてのバリデーションに加えて、属しているグループのバリデーションも同時に行われます。
- CHANGE:値が変更されたとき(input要素のchangeイベント相当)
- VALIDATE:フォームが送信される直前、または任意にvalidate()が呼ばれたとき
- FOCUS:フォーカスがあたったとき
- BLUR:フォーカスが外れたとき
- KEY_UP:キーボード入力時、キーが離されたとき
- VALIDATE : 送信ボタンが押されたとき、またはFormController.validate()メソッドが呼ばれたとき
上記の各タイミングはh5.validation.ValidationTiming列挙体で定義されています(ver.1.3.2以降)。
バリデータごとに、どのタイミングでそのバリデータを実行するかをカスタマイズすることができます。
タイミングを変更するには、ruleDefaultで当該バリデータに対してvalidateOnプロパティを記述してください。
なお、hifiveでデフォルトで提供しているルール(required, size等)はすべて、validateOnは「未定義(null)」にしています。
※validateOnを未定義(null)にしている場合の動作については次に説明します。
各タイミングである要素に対してバリデータが動作するかどうかは、下記のように決定されます。
- バリデータの実行タイミング設定(validateOn)が未定義(validateOnがnull)、かつ、タイミングが「VALIDATE」またはセットされているいずれかの出力プラグインの更新タイミング設定(updateOn)にそのタイミングが含まれている場合
- 上記以外で、そのバリデータのvalidateOnにそのタイミングが含まれている場合
(※注:validateOnでタイミングを明示した場合、出力プラグインが何もセットされていなくてもそのバリデータは実行されます) - 上記2つ以外で、「要再確認状態」と判断された場合
※「要再確認状態」とは、ある入力要素があるルールについて前回のバリデーション時に検証エラーと判定されていたとき、を指す。この場合、契機のイベントがバリデータで定義された「再確認タイミング(デフォルトではすべてのタイミング。"resolveOn"の設定で変更可能)」に含まれていれば、validateOnのタイミングに含まれていなくてもバリデーションを実行し、Resolve処理を行う
バリデータの実行可否を要素ごと・バリデータごとに個別かつ動的に制御する方法 [ver.1.3.2]
FormController.setPreValidationHook(func)を使用してフック関数を登録すると、バリデーションの実行を細かく制御できます。
funcには、以下のような関数をセットします。
/* validationContext = { name: (入力項目名), value: (入力値), rule: { name: (ルール名) , arg: (ルールパラメータ) }, timing: (タイミング(ValidationTiming)) } */
validationContext.skip(); //skip()を呼び出すと、その項目のバリデーションをスキップします。
}
formController.setPreValidationHook(preValidationHook); //フック関数をセット
FormControllerに対する設定可能パラメータ
isAllValidatorsEnabledWhenEmpty: /* (ver.1.3.2)trueを設定すると、すべてのバリデータについて、空文字の場合でも検出チェックを行うようになります。ただし、ルール側のisForceEnabledWhenEmptyの設定の方が優先されます(ルールのisForceEnabledWhenEmptyがfalseの場合、このパラメータをtrueにしても入力値が空の場合はそのルールは実行されません)。デフォルト値はfalseです。 */
}
ルールのデフォルト挙動のオーバーライド
FormControllerの設定で、ruleDefaultを記述することで、ルールのデフォルト挙動をオーバーライドすることができます。
設定例:
ruleDefault: {
required: { /* オーバーライドするルール名をキーにする */
isEnabledWhenEmpty: true /* 入力値が空の場合でもこのルールを適用するかどうか */
validateOn: ['validate', 'focus', 'blur'] /* このバリデータを実行するタイミング */
resolveOn: ['validate', 'focus', 'blur', 'keyup'] /* このバリデータのエラー解消再実行を行うタイミング */
}
}
}
formController.setSetting(setting);
バリデータのルールの書き方
- 基本的なルールの書き方(HTMLの場合)
HTML要素にdata-{ルール名}="{値}"を付加することでルールの指定が可能
また、カスタムバリデータ(ver.1.3.2以降)を定義した場合、
「data-h5-v-(バリデータ名)」のように指定すると、そのバリデータがその要素に対して実行されます。
- 基本的なルールの書き方(JavaScriptの場合)
formControllerのaddRuleを使い、プロパティごとにルールのオブジェクトを指定する
userid: {
required: true,
size: [3, 10],
max: 20
}
});
- 任意のバリデーションを行う書き方
hifiveで用意されていないバリデーションを行う場合、customFuncルールでバリデート関数を指定する
birthday: {
customFunc: function(value) {
var y = value.year;
var m = value.month;
var d = value.day;
var numRegexp = /^[0-9]+$/;
return numRegexp.test(y) && numRegexp.test(m) && numRegexp.test(d)
&& !isNaN(new Date(y + '/' + m + '/' + d).getDate());
}
}
});
- 非同期バリデーションを行う書き方
項目ごとにサーバと通信をしてバリデーションを行いたい場合など、非同期でバリデーションを行う場合も、customFuncを使用する。
customFuncで指定する関数がpromiseを返すようにし、成功の場合はresolveを、失敗の場合は、rejectするようにしておく。
userId: {
customFunc: function(value) {
var dfd = h5.async.deferred();
h5.ajax('existUser', {
type: 'GET',
data: {
user: value
},
dataType: 'json'
}).done(function(resp) {
if (resp.isExist) {
dfd.resolve({
valid: true,
userId: value
});
} else {
dfd.reject({
valid: false,
userId: value
});
}
});
return dfd.promise();
}
}
});
独自のバリデーションルール(カスタムルール)を作成する方法 [ver.1.3.2以降]
特定のFormControllerの中でカスタムルールを作成するには、FormControllerのsettingのcustomRuleで設定します(ver.1.3.2以降)。
customRule: {
validator1: { /* customRuleにセットするオブジェクトのキーはバリデータ名になります。バリデータの名前は[a-zA-Z][0-9a-zA-Z]* */
func: function(value) {}, /* (必須)チェック関数です。引数には値が渡されます。エラーとみなす場合はfalse、それ以外の場合はtrueを返すようにしてください。 */
message: '{displayName}はカスタムルール1に違反しています。' || function(param) {}, /* エラーがある場合のデフォルトのエラー文字列です。文字列または関数をセットできます。 */
isForceEnabledWhenEmpty: true, /* このルールを、値が空の場合にも適用するかどうかを指定します。デフォルトはfalseです。 */
validateOn: ['validate'], /* このルールによる「エラー検出」をいつ行うかを指定します。無指定またはnullの場合、すべてのタイミングで実行されます。 */
resolveOn: ['change'] /* このルールの「エラー解消」をいつ行うかを指定します。validateOnで指定したタイミングは暗黙的に必ず含まれます。無指定またはnullの場合、validateOnで指定したタイミングで実行されます。 */
}
}
};
(3) バリデーション結果(エラー)の使用と画面出力
バリデーション結果を画面に表示させるには、出力プラグインをFormControllerにセットします。
画面表示の例
- 特定の領域に全てのエラーをまとめて出力
- エラーがある入力項目に対してCSSクラスを適用する(ボーダーを赤くする等)
- エラーがある入力項目の横や下にメッセージを表示
- エラーがある入力項目にフォーカスが当たったら
- エラー詳細をポップアップ表示
- スタイル解除、エラー解除
- エラーがある入力項目ラベル側にスタイルを当てる
- 非同期チェック中は送信できない
- 送信ボタンが押せない
- フォームをブロック
- input, 送信ボタンがdisabled
バリデーション結果の取得
バリデーションの結果はValidationResultオブジェクトにまとめて格納されます。
このオブジェクトは、validate()メソッドの戻り値で返されます。
また、FormController.getLastValidationResult() を呼び出すと、そのFormContollerで呼び出し時点までに行われたバリデーションの累積結果を取得できます。
ValidationResultが持つ値についてはValidationResultのJSDocを参照してください。
- バリデーション対象のすべての項目についてエラーが1件もない場合、invalidReasonは空オブジェクトになります(ver.1.3.2以降。ver.1.3.1まではnullだった。)
- violationCountは違反したルールの総数を表します。(ver.1.3.2以降で追加)
FormControllerから発生するイベント
validateイベント
validationUpdateイベント [ver.1.3.2]
バリデーションが実行された場合、FormControllerから「validationUpdate」イベントが発生します。
イベント引数はありません。最新のバリデーション結果は getValidationResult() で取得できます。
タイミング | 内容 |
---|---|
asyncResult |
エラー出力プラグインの使用
FormControllerで検出したバリデーションエラーは、「出力プラグイン」を介して画面に出力することができます。
hifiveでは、以下の出力プラグインをデフォルトで提供しています。
- Message
- Composition
- ErrorBalloon および BootstrapErrorBalloon
- Style
- AsyncIndicator
Composition出力プラグイン
この出力プラグインは、検出したすべての項目のバリデーションエラーをまとめてリスト表示します。
設定可能パラメータ:
container: '#errors', /* エラー出力先の親要素(セレクタまたはDOM要素を指定)。ここで指定された要素の子要素としてエラーを出力します。) */
wrapper: '<li></li>', /* 個々のエラー文字列をくるむタグ。この例だと「<li>XXは必須です。</li>」のように出力されます。 */
showAllErrors: true, /* (ver.1.3.2以降) trueにすると、常に、これまでに検出されたすべての項目のすべてのエラーを出力します。falseにすると、最後に行われたバリデーション結果のみを表示します。例えば、ある項目1に必須チェック、別の項目2に長さチェックが設定されており、どちらも入力値が変更されたタイミングでバリデーションが行われるように設定されていたとき、項目1で必須エラーが出ている状態で項目2のバリデーションが行われて長さエラーが検出された場合、true⇒項目1の必須エラーと項目2の長さエラーの両方を表示。false⇒項目2の長さエラーのみ表示。となります。デフォルト値はtrueです。 */
hideWhenEmpty: true, /* (ver.1.3.2以降) エラーが一件もない場合に、containerで指定された要素自体を非表示(display:none)に設定します。デフォルト値はfalseです。なお、エラーがある場合にはコンテナ要素に"h5-composition-has-error"クラスが付与されます。 */
updateOn: ['validate', 'change'], /* (ver.1.3.2以降) この出力プラグインの出力をいつ更新するかを指定します。配列で複数指定することも可能です。指定可能な値は、h5.validation.ValidationTiming列挙体の値です。デフォルトは"validate"(validate()メソッドが呼ばれたとき、またはフォームが送信される直前)です。 */
property: {} /* 入力項目ごとの個別設定です。 */
}
なお、showAllErrorsをtrueにした場合、エラーは1回以上フォーカスがあたった要素(厳密には「バリデーションが一度以上行われた要素」)についてのみ出力されます。なお、FormController.validate()を引数なしで呼ぶとすべての入力項目に対して("validate"のタイミングで実行すると指定された)バリデーションが実行されるので、すべての項目のすべてのエラーが出力されます。[ver.1.3.2]
入力項目ごとの個別設定:
displayName: '電話番号', /* 入力項目の表示名です。 */
message: '{displayName}は必須です。' /* この項目でエラーがあった場合のエラーメッセージです。決められたプレースホルダを記述すると、出力時に実際の項目名などと置き換えられます。また、関数を設定することも可能です。 */
}
エラーメッセージのカスタマイズ
エラーメッセージを生成する際、フォーマット文字列による生成と関数による生成が可能です。
関数の場合、下記のような仕様の関数を指定します。
/* param = { name, value, rule: { ruleName, ruleValue }, violation: [{}, ...], displayName, targetViolation } */
/* targetViolationはver.1.3.2から */
var message = 'この項目はルール1に違反しています。';
return message; //最終的に表示させるエラーメッセージを返す
}
バリデーション結果オブジェクト(ValidationResult)の内容
プロパティ名 | 型 | 内容 |
---|---|---|
isAsync | boolean | 結果待ちの非同期バリデータが残っているかどうか。残っている場合にtrue。プロパティ単位で全プロパティのエラーの有無が確定している状態(validatingPropertiesが空配列の状態)でも、結果が確定していない非同期バリデータが残っている場合にはこのプロパティはtrueになる。また、abort()が呼び出されるとその時点でfalseになる。(例:sizeバリデータと任意の非同期バリデータが1つのプロパティに同時にセットされており、(非同期バリデータの結果が返ってくる前に)sizeバリデータに違反してそのプロパティにエラーがあることが確定している場合等。この場合、validatingPropertiesは空配列で、isValidはすでにfalseになっている) |
isAborted | boolean | abort()が呼ばれるとtrueになる。abort()を呼ぶと、そのValidationResultに紐づく非同期バリデータの結果は無視される。 |
プラグインが実装する表示更新タイミング関数(実装していない関数は呼ばない)
onValidate: function(result) { //onValidateはinputごとでなくvalidate()の処理に対して1回だけ呼び出す
},
onFocus: function(element, globalSetting, setting, errorReason) { //inputごと
if(setting.errorClass) {
element.addClass(setting.errorClass);
}
else {
addClass(global.errorClass)
}
}
onBlur: function(element, errorReason) {}, //inputごと
onChange: function(){} //inputごと
onKeyup: //input
onClick: //input
};
コードイメージ
errorClass: 'error',
balloon: {
showParam: xxx
},
allMessage: {
container:
}
}
outputSetting: {
username: { //キー名
label: 'ユーザー名',
formatter: func, //省略可能
//プラグインごとの設定オーバーライド
balloon: { //プラグイン名ごと
showAt: 'right',
timing: 'focus'
},
message: {
element: ,
},
errorClass: 'error', //デフォルトerror、省略可
errorElement: '_self', //errorClassを付与する要素、セレクタ可能 //自分自身の場合はどうする?
messageElement: '.username.errorMessage', //メッセージを表示する要素
customErrorMessage: { //Objectでなく直接Stringを渡したら、どんなエラーでもそれを表示
required: '{label}は絶対必要です。'
},//
group: //電話番号みたいやなつで多分必要 何する?
},
address1: {
}
}//
メモ
- emailチェックルールについて
- looseEmail ..2つとかをOKにしたユルイEmailチェック
- バリデータの種類
- Form
- JSon-schema
- 非同期サポート