値の検証処理を実装します
src/js/msg/msg.json、src/js/msg/msg.{言語}.jsonを作成しましょう
{ "errorMinMaxLength" : "length min:${min} max:${max}" ,"errorMinMaxValue" : "value min:${min} max:${max}" ,"errorRadioValue" : "cannot change value" ,"errorRadiType" : "cannot wrong type" ,"errorCheckValue" : "cannot change value" ,"errorCheckType" : "cannot wrong type" ,"errorSelectedIndex" : "index min:${min} max:${max}" ,"errorSelectValue" : "option cannot wrong type" ,"errorRequired" : "required" }
src/js/util/validator.jsを作成しましょう
export class Validator { constructor() { } initValidate (target, property, newValue, oldValue, arg, event) { arg.msgObj.textContent = ""; return true; } propRequireValidate (target, property, newValue, oldValue, arg, event) { if (newValue === undefined || newValue === null || newValue === '') { console.error(arg.name + ' : ' + $f.msg('errorRequired')); return false; } else { return true; } } propLengthValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== undefined && newValue !== null && (newValue.length < arg.minLen || arg.maxLen < newValue.length)) { console.error(arg.name + ' : ' + $f.msg('errorMinMaxLength', {'min': arg.minLen, 'max': arg.maxLen})); return false; } else { return true; } } textValidate (target, property, newValue, oldValue, arg, event) { if (newValue.length < arg.minLen || arg.maxLen < newValue.length) { arg.msgObj.textContent = $f.msg('errorMinMaxLength', {'min': arg.minLen, 'max': arg.maxLen}); return false; } else { return true; } } radioValueValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== oldValue) { arg.msgObj.textContent = $f.msg('errorRadioValue'); return false; } else { return true; } } radioCheckValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== true && newValue !== false) { arg.msgObj.textContent = $f.msg('errorRadiType'); return false; } else { return true; } } checkValueValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== oldValue) { arg.msgObj.textContent = $f.msg('errorCheckValue'); return false; } else { return true; } } checkCheckValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== true && newValue !== false) { arg.msgObj.textContent = $f.msg('errorCheckType'); return false; } else { return true; } } selectValueValidate (target, property, newValue, oldValue, arg, event) { if (!arg.values.has(newValue)) { arg.msgObj.textContent = $f.msg('errorSelectValue'); return false; } else { return true; } } selectIndexValidate (target, property, newValue, oldValue, arg, event) { if (newValue < arg.min || arg.max < newValue) { arg.msgObj.textContent = $f.msg('errorSelectedIndex', {'min': arg.min, 'max': arg.max}); return false; } else { return true; } } selectOptionValidate (target, property, newValue, oldValue, arg, event) { if (newValue !== true && newValue !== false) { arg.msgObj.textContent = $f.msg('errorSelectValue'); return false; } else { return true; } } }
src/js/modules/index.jsを修正しましょう
import {Validator} from '../util/validator.js'; export class Index { constructor() { this.prop1 = 'abc'; } init() { let v = new Validator(); $f.validate('group1', this.text, 'value', ['blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1}); $f.validate('group1', this.radio1, 'value', ['input', null], [v.initValidate.bind(v), v.radioValueValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio1, 'checked', ['input', null], [v.initValidate.bind(v), v.radioCheckValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio2, 'value', ['input', null], [v.initValidate.bind(v), v.radioValueValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio2, 'checked', ['input', null], [v.initValidate.bind(v), v.radioCheckValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.checkbox, 'value', ['input', null], [v.initValidate.bind(v), v.checkValueValidate.bind(v)], {"msgObj": this.bind4}); $f.validate('group1', this.checkbox, 'checked', ['input', null], [v.initValidate.bind(v), v.checkCheckValidate.bind(v)], {"msgObj": this.bind4}); $f.validate('group1', this.singleSelect, 'value', ['input', null], [v.initValidate.bind(v), v.selectValueValidate.bind(v)], {"values": new Set(["aa", "bb", "cc"]), "msgObj": this.bind5}); $f.validate('group1', this.singleSelect, 'selectedIndex', [null], [v.initValidate.bind(v), v.selectIndexValidate.bind(v)], {"min": 0, "max": 2, "msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[0], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[1], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[2], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.multipleSelect, 'value', ['input', null], [v.initValidate.bind(v), v.selectValueValidate.bind(v)], {"values": new Set(["aaa", "bbb", "ccc"]), "msgObj": this.bind7}); $f.validate('group1', this.multipleSelect, 'selectedIndex', [null], [v.initValidate.bind(v), v.selectIndexValidate.bind(v)], {"min": 0, "max": 2, "msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[0], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[1], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[2], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.textarea, 'value', ['blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 5, "maxLen": 10, "msgObj": this.bind9}); $f.validate('group1', this, 'prop1', [null], [v.propRequireValidate.bind(v), v.propLengthValidate.bind(v)], {"minLen": 2, "maxLen": 4, "name": "Index->prop1"}); } btn_click(event) { this.text.value = '12345'; this.radio1.value = 'a'; this.radio1.checked = 'a'; this.radio2.value = 'a'; this.radio2.checked = 'a'; this.checkbox.value = 'a'; this.checkbox.checked = 'a'; this.singleSelect.value = 3; this.singleSelect.selectedIndex = 3; this.singleSelect.options[0].selected = 'a'; this.singleSelect.options[1].selected = 'a'; this.singleSelect.options[2].selected = 'a'; this.multipleSelect.value = 3; this.multipleSelect.selectedIndex = 3; this.multipleSelect.options[0].selected = 'a'; this.multipleSelect.options[1].selected = 'a'; this.multipleSelect.options[2].selected = 'a'; this.textarea.value = '12345678901'; this.prop1 = 'abcdef'; } }
src/page/index.htmlを修正しましょう
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title> sample </title> <script id="fs-js" src="http://localhost:8000/js/fairysupport.min.js" data-page-root="http://localhost:8000/page"></script> </head> <body> <div data-obj="bind1"></div> <div><input type="text" data-obj="text" value="1234"></div> <div data-obj="bind2"></div> <div><input type="radio" data-obj="radio1" name="radio" value="1"><input type="radio" data-obj="radio2" name="radio" value="2"></div> <div data-obj="bind4"></div> <div><input type="checkbox" data-obj="checkbox" value="1"></div> <div data-obj="bind5"></div> <div data-obj="bind6"></div> <div> <select data-obj="singleSelect"> <option value="aa" selected>aa_val</option> <option value="bb">bb_val</option> <option value="cc">cc_val</option> </select> </div> <div data-obj="bind7"></div> <div data-obj="bind8"></div> <div> <select data-obj="multipleSelect" multiple> <option value="aaa" selected>aaa_val</option> <option value="bbb">bbb_val</option> <option value="ccc">ccc_val</option> </select> </div> <div data-obj="bind9"></div> <div><textarea cols="10" rows="10" data-obj="textarea">12345</textarea></div> <div><input type="button" data-name="btn" value="set"></div> </body> </html>
index.jsで使っている$f.validateメソッドは、第2引数オブジェクトに第4引数イベントが発生するとき、第5引数に渡した関数を実行するメソッドです
例えば、$f.validate('group1', this.text, 'value', ['blur'], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1});の場合
this.textオブジェクトにblurイベントが発生したとき、ValidatorクラスのinitValidate、textValidateメソッドが実行されます
第4引数には配列で複数イベントを渡すことができます。nullを渡すと第2引数の第3引数プロパティに=演算子で値を格納する時という意味になります
例えば、$f.validate('group1', this.text, 'value', [null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1});の場合
this.textオブジェクトのvalueプロパティに=演算子で値が格納されるとき、ValidatorクラスのinitValidate、textValidateメソッドが実行されます
$f.validateの第5引数に渡される関数は引数を6つ取ります
第1引数:$f.validateの第2引数に渡した値
第2引数:$f.validateの第3引数に渡した値
第3引数:第2引数の第3引数プロパティに格納する値
第4引数:focus、blur、=演算子、Validatorによるtrue返却の4つの内、直近で発生したイベントによって格納された値。この値は、$f.validateの第4引数の要素(イベント)ごとに管理されます。つまり、Validatorによるtrue返却によって設定された値は、Validatorが実行されたイベントのoldValueを上書きますが、他のイベントのoldValueは上書きません。
第5引数:$f.validateの第6引数に渡した値
第6引数:イベントオブジェクト
以降、$f.validateの第5引数に渡された関数をValidatorと呼びます
Validatorの戻り値はbooleanです。全てのValidatorがtrueを返すと$f.validateの第2引数オブジェクトの第3引数プロパティにValidatorの第3引数を格納します。Validatorが1つでもfalseを返すと$f.validateの第2引数オブジェクトの第3引数プロパティにValidatorの第4引数を格納します。
手動実行
src/js/modules/index.jsを修正しましょう
import {Validator} from '../util/validator.js'; export class Index { constructor() { this.prop1 = 'abc'; } init() { let v = new Validator(); $f.validate('group1', this.text, 'value', ['blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1}); $f.validate('group1', this.radio1, 'value', ['input', null], [v.initValidate.bind(v), v.radioValueValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio1, 'checked', ['input', null], [v.initValidate.bind(v), v.radioCheckValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio2, 'value', ['input', null], [v.initValidate.bind(v), v.radioValueValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.radio2, 'checked', ['input', null], [v.initValidate.bind(v), v.radioCheckValidate.bind(v)], {"msgObj": this.bind2}); $f.validate('group1', this.checkbox, 'value', ['input', null], [v.initValidate.bind(v), v.checkValueValidate.bind(v)], {"msgObj": this.bind4}); $f.validate('group1', this.checkbox, 'checked', ['input', null], [v.initValidate.bind(v), v.checkCheckValidate.bind(v)], {"msgObj": this.bind4}); $f.validate('group1', this.singleSelect, 'value', ['input', null], [v.initValidate.bind(v), v.selectValueValidate.bind(v)], {"values": new Set(["aa", "bb", "cc"]), "msgObj": this.bind5}); $f.validate('group1', this.singleSelect, 'selectedIndex', [null], [v.initValidate.bind(v), v.selectIndexValidate.bind(v)], {"min": 0, "max": 2, "msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[0], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[1], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.singleSelect.options[2], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind5}); $f.validate('group1', this.multipleSelect, 'value', ['input', null], [v.initValidate.bind(v), v.selectValueValidate.bind(v)], {"values": new Set(["aaa", "bbb", "ccc"]), "msgObj": this.bind7}); $f.validate('group1', this.multipleSelect, 'selectedIndex', [null], [v.initValidate.bind(v), v.selectIndexValidate.bind(v)], {"min": 0, "max": 2, "msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[0], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[1], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.multipleSelect.options[2], 'selected', [null], [v.initValidate.bind(v), v.selectOptionValidate.bind(v)], {"msgObj": this.bind7}); $f.validate('group1', this.textarea, 'value', ['blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 5, "maxLen": 10, "msgObj": this.bind9}); $f.validate('group1', this, 'prop1', [null], [v.propRequireValidate.bind(v), v.propLengthValidate.bind(v)], {"minLen": 2, "maxLen": 4, "name": "Index->prop1"}); } btn_click(event) { console.log($f.execValidator('group1', this.text)); let vaild = $f.execGroupValidator('group1'); if (!vaild) { event.preventDefault(); event.stopImmediatePropagation(); return; } } }
execValidatorは、$f.validateの第1引数、第2引数に合致するValidatorを実行します
execValidatorは、終了処理の戻り値でoldValueのキーがあっても無視します。oldValueAllEvent、oldValueSpecificEventのキーを無視しません。第3引数にtrueが渡された場合、forceNewValue、newValueのキーを無視しません。
execGroupValidatorは、$f.validateの第1引数に合致するValidatorを実行します
execGroupValidatorは、終了処理の戻り値でoldValueのキーがあっても無視します。oldValueAllEvent、oldValueSpecificEventのキーを無視しません。第2引数にtrueが渡された場合、forceNewValue、newValueのキーを無視しません。
終了処理については下記で説明します
終了処理
src/js/util/validator.jsを変更しましょう
export class Validator { constructor() { } initValidate (target, property, newValue, oldValue, arg, event) { arg.msgObj.textContent = ""; return true; } textValidate (target, property, newValue, oldValue, arg, event) { if (newValue.length < arg.minLen || arg.maxLen < newValue.length) { arg.msgObj.textContent = $f.msg('errorMinMaxLength', {'min': arg.minLen, 'max': arg.maxLen}); return false; } else { return true; } } textFinalize (target, property, newValue, oldValue, arg, event, valid, validResult) { let result = {}; if (event && 'type' in event && 'input' === event.type) { result['forceNewValue'] = newValue; if (valid) { result['oldValueAllEvent'] = newValue; } } return result; } }
src/js/modules/index.jsを変更しましょう
import {Validator} from '../util/validator.js'; export class Index { constructor() { this.prop1 = 'abc'; } init() { let v = new Validator(); $f.validate('group1', this.text, 'value', ['input', 'blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1}, v.textFinalize.bind(v)); } }
src/page/index.htmlを修正しましょう
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title> sample </title> <script id="fs-js" src="http://localhost:8000/js/fairysupport.min.js" data-page-root="http://localhost:8000/page"></script> </head> <body> <div data-obj="bind1"></div> <div><input type="text" data-obj="text" value="1234"></div> </body> </html>
以降、$f.validateの第5引数に渡された関数をValidatorと呼びます
$f.validateの第7引数に終了処理を渡すことができます
$f.validateの第7引数に渡される関数は引数を8つ取ります
第1~6引数:Validatorの引数と同じ
第7引数:boolean。Validatorの内、1つでもfalseを返していれば、false
第8引数:オブジェクト。キーはValidatorの戻り値。値は配列でValidator名
$f.validateの第六引数に渡される関数の戻り値はオブジェクトです。オブジェクトの内容は下記です
キー:newValue、値:Validatorの第3引数を置きかえます
キー:forceNewValue、値:Validatorの第3引数を置きかえます。さらに、Validator終了処理の第7引数がfalseであっても、第2引数の第3引数プロパティの値をこの値で上書きます。
キー:oldValue、値:Validatorの第4引数。Validatorの第4引数は、$f.validateの第4引数の要素(イベント)ごとに管理されます。Validatorの第6引数イベントにひもづくValidatorの第4引数を置きかえます
キー:oldValueAllEvent、値:Validatorの第4引数。Validatorの第4引数は、$f.validateの第4引数の要素(イベント)ごとに管理されます。$f.validateの第4引数に渡された全イベントにひもづくValidatorの第4引数を置きかえます
キー:oldValueSpecificEvent、値:オブジェクト。キーがイベント名、値がValidatorの第4引数。Validatorの第4引数は、$f.validateの第4引数の要素(イベント)ごとに管理されます。$f.validateの第4引数に渡されたイベントの内このオブジェクトのキーに合致するイベントにひもづくValidatorの第4引数を置きかえます
直近結果取得
src/js/modules/index.jsを変更しましょう
import {Validator} from '../util/validator.js'; export class Index { constructor() { this.prop1 = 'abc'; } init() { let v = new Validator(); $f.validate('group1', this.text, 'value', ['input', 'blur', null], [v.initValidate.bind(v), v.textValidate.bind(v)], {"minLen": 2, "maxLen": 4, "msgObj": this.bind1}, v.textFinalize.bind(v)); } btn_click(event) { console.log($f.getValidateLatestResult('group1', this.text, 'value', 'blur')); } }
src/page/index.htmlを修正しましょう
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title> sample </title> <script id="fs-js" src="http://localhost:8000/js/fairysupport.min.js" data-page-root="http://localhost:8000/page"></script> </head> <body> <div data-obj="bind1"></div> <div><input type="text" data-obj="text" value="1234"></div> <div><input type="button" data-name="btn" value="LatestResult"></div> </body> </html>
getValidateLatestResultは、getValidateLatestResultに渡された引数が$f.validateの第1~3引数に合致し、$f.validateの第4引数のどれかに合致するValidatorの直近の結果を返します