How to use JavaScript strategy pattern to validate forms

How to use JavaScript strategy pattern to validate forms

Overview

In web projects, login, registration and other functions all require form submission. Before submitting the user's data to the backend, the frontend generally needs to do some verification within its capacity, such as whether the form is filled in, the length of the form, whether the password complies with the specifications, etc. Front-end verification can avoid submitting non-compliant forms.

Suppose we have a form with the following validation logic:

  • Username is not empty
  • Password length is not less than 6 characters
  • Mobile phone number conforms to the format

Form validation without using strategy pattern

When the strategy mode is not used, the first verification mode we think of is usually like this:

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

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

  • The onsubmit function is too large and contains many if-else statements to cover all rules.
  • The onsubmit function lacks flexibility. If you want to enter a new validation rule, you have to change the content of the function, which violates the open-closed principle.
  • The code reusability is poor. If you write another form, you have to copy a lot of duplicate code.

Optimize using the strategy pattern

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', 'Username cannot be empty');
    validator.add(Form.password, 'minLength:6', 'The password length cannot be less than 6 characters');
    validator.add(Form.phone, 'isMobile', 'The mobile phone number format is incorrect');
    const errMsg = validator.start();
    return errMsg;
}

As shown in the code, the validator instance has an add method that receives three parameters. The first one is the form instance that needs to be validated, the second one is the validation method, and the parameters after the colon are passed in. The third one is the error message if the verification fails.

The start method is used to start the verification. If it fails, a prompt message indicating that it has failed will be returned, which can be processed in the subsequent logic.

Writing of 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 strategy mode, we use the configuration method to complete the form verification. These rules can be used in any place where the form is verified in the future, which is more convenient for modification and reuse.

Add multiple validation rules to a single form field

Our code currently has a shortcoming, that is, it can only assign a single validation rule to a form item, and cannot implement multiple validation rules for a form, so there is room for improvement in the code.

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: 'Username cannot be empty'
    }]);
    validator.add(Form.password, [{
        strategy: 'minLength:6',
        errMsg: 'The password length cannot be less than 6 characters'
    }]);
    validator.add(Form.phone, [{
        strategy: 'isMobile',
        errMsg: 'The mobile phone number format is incorrect'
    }, {
        strategy: 'empty',
        errMsg: 'Phone number cannot be empty'
    }]);
    const errMsg = validator.start();
    return errMsg;
}

You only need to pass in an object array when passing parameters, and add the corresponding array processing logic in the add function.

Advantages of the Strategy Pattern

advantage:

  • Avoid multiple conditional select statements
  • Implementing the open-closed principle makes the use of functions easier to switch, easier to understand, and easier to expand.
  • Improve code reuse

Summarize

Peter Norvig said that in a language where functions are first-class objects, the strategy pattern is invisible, and strategy is a variable whose value is a function. In fact, it is the process of passing the encapsulated strategy function as a parameter to the target that uses it and being called by the target. Making good use of the strategy pattern not only allows us to have a deeper understanding of the pattern, but also makes us understand the benefits of using the function.

The above is the details of how to use JavaScript strategy mode to verify the form. For more information about JavaScript, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • How to implement the strategy pattern in Javascript
  • Detailed explanation of the use of JavaScript strategy mode from form validation
  • JavaScript design pattern strategy pattern implementation principle detailed explanation
  • JS form validation plug-in data and logic separation operation example analysis [strategy mode]
  • JavaScript Design Patterns – Strategy Pattern Principles and Usage Examples
  • Analysis of the concept and usage of strategy pattern in JS design pattern
  • JavaScript rewrites the asynchronous validation form into a synchronous form
  • Summary of three methods of submitting the form after js verifies the form
  • Easily master JavaScript strategy mode

<<:  How to configure mysql5.6 to support IPV6 connection in Linux environment

>>:  Detailed explanation of mysql permissions and indexes

Recommend

Docker image import, export, backup and migration operations

Export: docker save -o centos.tar centos:latest #...

js method to realize shopping cart calculation

This article example shares the specific code of ...

Detailed explanation of the execution principle of MySQL kill command

Table of contents Kill instruction execution prin...

How to add conditional expressions to aggregate functions in MySql

MySQL filtering timing of where conditions and ha...

JavaScript implements simple calculator function

This article example shares the specific code of ...

Two ways to create SSH server aliases in Linux

Preface If you frequently access many different r...

MySQL paging query optimization techniques

In applications with paging queries, queries that...

JavaScript single thread and asynchronous details

Table of contents 1. Task Queue 2. To explain som...

Detailed process of building nfs server using Docker's NFS-Ganesha image

Table of contents 1. Introduction to NFS-Ganesha ...

How to create Apache image using Dockerfile

Table of contents 1. Docker Image 2. Create an in...

Summary of front-end knowledge in the Gokudō game

background In the early stages of learning Japane...

JavaScript to implement dynamic digital clock

This article shares the specific code for impleme...

JS operation object array to achieve add, delete, modify and query example code

1. Introduction Recently, I helped a friend to ma...