Verifying forms using JavaScript policy patterns

Form validation

In the Web project, the functions of login, registration and so on need to submit forms. Before submitting the user's data to the background, the front-end generally needs to do some verifications within its ability, such as whether to fill in, the length of filling in, whether the password conforms to the specification, etc. The front-end verification can avoid submitting the non-standard forms.

If we have a form, the verification logic is as follows:

  • User name is not empty
  • Password length not less than 6 digits
  • Mobile phone number conforms to the format

Form validation without policy mode

When the policy mode is not used, that is, the first verification mode we will think of is usually as follows:

<body>
    <form id="registerForm">
        <label for="username">Enter user name:<input type="text" name="username"></label>
        <label for="password">Input password:<input type="password" name="password"></label>
        <label for="phone">Input password:<input type="text" name="phone"></label>
    </form>
    <script>
        const form = document.querySelector('.registerForm');
        form.onsubmit = function(){
            if(form.username.value === ''){
                alert('User name cannot be empty')
                return;
            }
            if(form.password.value.length < 6){
                alert('Password length cannot be less than 6 digits')
                return;
            }
            if(!/(^1[3|5|8][0-9]{9}$)/.test(form.phone.value)){
                alert('Incorrect phone number format')
                return;
            }
        }
    </script>
</body>

This way of coding is very common, but its disadvantages are also obvious:

  • The onsubmit function is too large and contains many if else rules to cover
  • The onsubmit function lacks flexibility. If you want to enter a new verification rule, you need to change the content implementation of the function, in violation of the open close principle
  • Code reusability is poor. If you write another form, you need to copy many duplicate codes

Use policy pattern optimization

First, encapsulate the verification function as an object:

const strategies = {
    empty(value, errMsg){
        if(value.length === 0){
            return errMsg;
        }
    },
    minLength(value, len, errMsg){
        if(value.length < len){
            return errMsg;
        }
    },
    isMobile(value, errMsg){
        if(!/(^1[3|5|8][0-9]{9}$)/.test(value)){
            return errMsg;
        }
    }
}

We also need a Validator class, which is used to add validation rules to the target form. Its usage is as follows:

const validate = function(){
    const validator = new Validator();
    validator.add(Form.userName, 'empty', 'User name cannot be empty');
    validator.add(Form.password, 'minLength:6', 'Password length cannot be less than 6 digits');
    validator.add(Form.phone, 'isMobile', 'Incorrect format of mobile number');
    const errMsg = validator.start();
    return errMsg;
}

As shown in the code,

The validator instance has the add method, which receives three parameters. The first is the form instance to be validated, the second is the validation method, and the colon is followed by the incoming parameter. The third is the error message of failed verification.

Start method is used to start the verification. If it fails, it will return the failed prompt message, which can be processed in the subsequent logic

To write the Validator class:

class Validator {
    constructor(){
        this.rules = [];
    }
    add(elem, rule, err){
        const args_arr = rule.split(":");
        this.rules.push(()=>{
            const handler = args_arr.shift();
            args_arr.unshift(elem.value);
            args_arr.push(err);
            return strategies[handler].apply(elem, args_arr)
        })
    }
    start(){
        let errmsg = []
        for(let i = 0; i < this.rules.length; i++ ){
            const err = this.rules[i]();
            if(err){
                errmsg.push(err)
            }
        }
        return errmsg.join(",");
    }
}

Using the policy mode, we use the configuration method to complete the form verification. These rules can be used in any place to verify the form in the future, which is more convenient to modify and reuse

Add multiple validation rules for a single form item

Our code now has a disadvantage that we can only assign a single validation rule to a form item, and cannot implement multiple validation rules for a form, so the code has room for optimization

class Validator{
    // ···
    add(elem, rules){
        rules.forEach(rule => {
            const args_arr = rule.strategy.split(":");
            this.rules.push(()=>{
                const handler = args_arr.shift();
                args_arr.unshift(elem.value);
                args_arr.push(rule.errMsg);
                return strategies[handler].apply(elem, args_arr)
            })
        });
    }
    // ···
}

const validate = function(){
    const validator = new Validator();
    validator.add(Form.username,[{
        strategy: 'empty',
        errMsg: 'User name cannot be empty'
    }]);
    validator.add(Form.password, [{
        strategy: 'minLength:6',
        errMsg: 'Password length cannot be less than 6 digits'
    }]);
    validator.add(Form.phone, [{
        strategy: 'isMobile',
        errMsg: 'Incorrect format of mobile number'
    }, {
        strategy: 'empty',
        errMsg: 'Mobile number cannot be empty'
    }]);
    const errMsg = validator.start();
    return errMsg;
}

Just pass in an object array and add the corresponding array processing logic in the add function

Advantages of the strategic model

Advantage:

  1. Avoid multiple conditional selection statements
  2. The principle of opening and closing is realized, which makes the use of functions easier to switch, understand and expand.
  3. Improve code reuse

Conclusion:

Peter Norvig said that in the language of function as a first-class object, the policy pattern is invisible, and strategy is the variable whose value is function. In fact, it is to pass the encapsulated policy function as a parameter to the target using it, and the process called by target. Using the policy mode well not only makes us have a deeper understanding of the mode, but also makes us understand the benefits of using the function.

This article is quoted from "Javascript Design Pattern and development practice"

Tags: Javascript less Mobile

Posted on Mon, 16 Mar 2020 08:32:44 -0400 by catnip_uk