JavaScript フレームワーク fairy support js
validate

値の検証処理を実装します
src/js/msg/msg.json、src/js/msg/msg.{言語}.jsonを作成しましょう

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を作成しましょう

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を修正しましょう

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を修正しましょう

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を修正しましょう

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を変更しましょう

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を変更しましょう

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を修正しましょう

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を変更しましょう

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を修正しましょう

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の直近の結果を返します


次ページ値の保持

目次