After reading the JavaScript Plug-in Writing Guide, the most exciting moment has come! Let’s start making a data validation plug-in!

We first initialize an HTML to use as a data source for validation

<body>
    <div>
        <form>
            <div>
                <label for="exampleInputEmail1">email</label>
                <input type="email" id="exampleInputEmail1" placeholder="Please enter a valid email address">
            </div>
            <div>
                <label for="exampleInputPassword1">Mobile phone number</label>
                <input type="text" id="exampleInputPhone1" placeholder="Please enter a valid mobile phone number">
            </div>
            <div>
                <label for="exampleInputPassword1">password</label>
                <input type="password" id="exampleInputPassword1" placeholder="Please enter a valid password">
            </div>
            <div>
                <label for="exampleInputPassword2">Confirm password</label>
                <input type="password" id="exampleInputPassword2" placeholder="Please enter your password again">
            </div>

            <button type="submit">registered</button>
        </form>
    </div>
    <script src="Jquery2.2.4. Js"></script>
    <script src="dataValidate.js"></script>
</body>
Copy the code

First initialize the plug-in

(function(root, factory, plug){
    // The factory function that executes the following function immediately, passing in the jquery object and the plug-in name
    factory($,plug)
    // This represents the window object globally in non-strict mode}) (this.function($,plug){
    // Tests whether the factory function was successfully called
    console.log(plug);
},"dataValidate")
Copy the code

Since we are developing a plug-in that relies on jQuery, we need to bind our plug-in to a jQuery real object, that is, to a jQuery prototype.

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    // Extend dataValidate method in jQuery
    $.prototype[plug] = function() {}},"dataValidate")
Copy the code

We can test the binding successfully in jQuery in HTML

<script>
    console.log($().dataValidate)
</script>
Copy the code

Restrict form validation

Since our plugin only validates the input in the form, it does not validate all input values, so we need to make a restriction:

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    $.prototype[plug] = function() {
        // This represents a jquery real object
        // If not in the form, return directly without validation
        if (!this.is("form")) {
            return; }}},"dataValidate")
Copy the code

Next, we know that we need to validate the input, so find all the input under the form and bind it to a jQuery property:

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        // Bind all input to $file in jquery
        this.$file = this.find("input");
        // Bind an input event to all inputs
        this.$file.on("input".function() {
            // Test
            // Inside the event function, this represents the Element that triggered the event.
            // In the constructor, this represents the real object of the constructor
            console.log(this.value)
        })
    }
}, "dataValidate")
Copy the code

If the built-in verification rules do not meet user requirements, users can customize the verification rules

However, I have a question at this time. The input event cannot satisfy everyone’s needs, because this plug-in is developed for the team and the user. The user may need a blur event to trigger.

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = disambiguation = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = the input event certainly cannot write dead, this time we need to use the default configuration, Replace uncertainties with default configurations.

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    // Store the default configuration
    var DAFAULT = {
        initEvent: "input"
    }
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        Dafault.initevent = dafault.initEvent
        this.$file.on(DAFAULT.initEvent, function() {})}},"dataValidate")
Copy the code

In fact, we can also use another way, first I will think of a solution, I will extend the default configuration of these parameters and attributes to another object ——this, because this represents the real object of jQuery, I will extend the default configuration to the real object of jQuery, who uses me, I’m just going to extend those properties to somebody.

So, how do you scale? $.extend() = $.extend() = $.extend() = $.extend()

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    $.prototype[plug] = function() {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT);
        // Test
        console.log(this.initEvent) // input
        this.$file.on(this.initEvent, function() {})}},"dataValidate")
Copy the code

The default configuration is used, but there is a question, how does the user use his own configuration?

Of course, the user wants to use what, on their own.

<script>
    $("form").dataValidate({
        // How do users know about initEvent?
        // That is to tell the user through the plug-in interface documentation that this field is configured to trigger events
        initEvent: "blur"
    })
</script>
Copy the code
(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    // Receive the user configuration parameters
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        // Expand directly
        $.extend(this, DAFAULT, options);
        // Test
        console.log(this.initEvent) // input
        this.$file.on(this.initEvent, function() {})}},"dataValidate")
Copy the code

After this problem is solved, we need to start thinking about a number of issues, for example, we need to provide basic default rules for the plug-in.

Data validation plugin We need to provide the following basic functions:

  1. Regular (REg)
  2. Input cannot be null (required)
  3. Minimum length (min-length)
  4. Confirm text
  5. Tip (the message)
  6. Extend user – defined configurations

Now that we know the basics, we fill in the rules in our HTML,

We can do this by configuring custom attributes (data-) (also called directives) :

But we want to know what is our configuration, which is the label extension of the custom attribute, if begin is based on the data and it won’t be able to distinguish configuration extension of the custom attribute, so this time want to add a label to indicate that this is my plug-in configuration dv, so in interface document, also want to tell the user to configure by data – dv

<form>
    <div>
        <label for="exampleInputEmail1">email</label>
        <! -- can't set empty mailbox to regular -->
        <input type="email" id="exampleInputEmail1" placeholder="Please enter a valid email address" data-dv-required=true data-dv-required-message="Mailbox cannot be empty." data-dv-reg="^\w+@\w+\.\w+$" data-dv-reg-message="Email format is incorrect">
    </div>
    <div>
        <label for="exampleInputPassword1">Mobile phone number</label>
        <! -- Cannot set empty phone number to regular -->
        <input type="text" id="exampleInputPassword1" placeholder="Please enter a valid mobile phone number" data-dv-required=true data-dv-required-message="Cell phone number cannot be empty." data-dv-reg="^1\d{10}$" data-dv-reg-message="Mobile phone number format is incorrect">
    </div>
    <div>
        <label for="exampleInputPassword1">password</label>
        <! -- Cannot be empty password regular character length -->
        <input type="password" id="exampleInputPassword1" placeholder="Please enter a valid password" data-dv-required=true data-dv-required-message="Password cannot be empty." data-dv-reg="^\w+$" data-dv-reg-message="Incorrect password format" data-dv-min-length=6 data-dv-min-length-message="Incorrect password length">
    </div>
    <div>
        <label for="exampleInputPassword1">Confirm password</label>
        <! -- cannot be null password consistent -->
        <input type="password" id="exampleInputPassword1" placeholder="Please enter your password again" data-dv-required=true data-dv-required-message="Password cannot be empty." data-dv-confirm=true data-dv-confirm-message="Two different passwords.">
    </div>
</form>
Copy the code

After the configuration, you will find that the configuration does not seem to be useful, so we need to write the corresponding default rules in JS according to the configuration on the page.

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    var DAFAULT = {
        initEvent: "input"
    }
    // Default rule
    var RULES = {
        "reg": function() {},
        "required": function() {},
        "min-length": function() {},
        "confirm": function() {}}// Receive the user configuration parameters
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT, options);
        this.$file.on(this.initEvent, function() {
            // Iterate over the default configuration
            $.each(RULES, function(key, fn) {
                // In this case this refers to the Element object
                // We want to use this.data to retrieve user-defined attributes of data, but this method is only available to jQuery objects.}); })}},"dataValidate")
Copy the code

It’s easy to wrap the element object as a jQuery object.

(function(root, factory, plug) {
    factory($, plug)
})(this.function($, plug) {
    var DAFAULT = {
        initEvent: "input".plugName: "dv"
    }
    // Default rule
    var RULES = {
        "reg": function() {},
        "required": function() {},
        "min-length": function() {},
        "confirm": function() {}}// Receive the user configuration parameters
    $.prototype[plug] = function(options) {
        if (!this.is("form")) {
            return;
        }
        this.$file = this.find("input");
        $.extend(this, DAFAULT, options);
        this.$file.on(this.initEvent, function() {
            / / packaging
            var _this = $(this);
            $.each(RULES, function(key, fn) {
                // _this.data("dv-" + key) this is a bit silly, so in the default configuration, write dv
                var $fileName = _this.data(this.plugName + "-" + key);
                var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
                // Test the output
                console.log($fileName);
                console.log($fileMessage); }); })}},"dataValidate")
Copy the code

Next we need to call the function using call and change the this reference

Take a test:

var RULES = {
    "reg": function() {
        console.log(this);
    },
    "required": function() {
        console.log(this);
    },
    "min-length": function() {
        console.log(this);
    },
    "confirm": function() {
        console.log(this); }}Copy the code
$.each(RULES, function(key, fn) {
    var $fileName = _this.data(this.plugName + "-" + key);
    var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
    if ($fileName) {
        // fn.call()
        // fn.call(this)
        fn.call(_this)
    }
});
Copy the code

Now that we know what this is pointing to, we are ready to implement the default validation rule

var RULES = {
    "reg": function(data) {
        return new RegExp(data).test(this.val());
    },
    "required": function(data) {
        return this.val();
    },
    "min-length": function(data) {
        return this.val().length > data;
    },
    "confirm": function(data) {
        // How to verify password consistency?}}Copy the code
$.each(RULES, function(key, fn) {
    var $fileName = _this.data(this.plugName + "-" + key);
    var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
    if ($fileName) {
        var result = fn.call(_this, $fileName);
        if(! result) {// Perform error processing}}});Copy the code

To verify consistency, do the following:

"confirm": function(data) {
    var passwordElement = $(":password") [0];
    if (passwordElement.value == "" || this.val() ! = passwordElement.value) {return false;
    } else {
        return true; }}Copy the code

Finally, error handling is performed:

this.$file.on(this.initEvent, function() {
    var _this = $(this);
    // Check if there is an error message, if there is, then kill all
    _this.siblings('p').remove();
    $.each(RULES, function(key, fn) {
        var $fileName = _this.data(this.plugName + "-" + key);
        var $fileMessage = _this.data(this.plugName + "-" + key + "-message");
        if ($fileName) {
            var result = fn.call(_this, $fileName);
            if(! result) {// Append the error message directly to the end
                _this.after("<p style='color:red'>" + $fileMessage + "</p>")}}}); })Copy the code

This step is basically complete, the user just need to import our JS file, and then give the interface document to the user, let the user customize the error message and the regular check.

But what if the user wants to extend the default validation rule by, say, adding max-length?

With our approach, do you think you can scale?

Of course, because our method is bound to a jQuery real object, we can extend it in the same way that jQuery extends it, and tell the user to do so

$.fn.dataValidate.extendValidate = function(options) {
    $.extend(RULES, options)
}
Copy the code
<script type="text/javascript">
    // User extension
    $.fn.dataValidate.extendValidate({
        "max-length": function(data) {
            return this.val().length <= data; }})</script>
Copy the code

We can configure data-DV-max-length for testing.

At this point, a simple data validation plug-in has been developed.

conclusion

Before we develop a requirement or solve a problem, we should think more about how to change to make the code more reusable and faster, rather than using if{}else{} to judge the modification without thinking at the beginning, so that after a large amount of code, it will be very troublesome to maintain.

The code in our form is thousands of times better than the code that uses if{}else{} every time for data verification. It has better maintainability and scalability. Of course, this kind of code can not be written overnight, but needs to accumulate over a long period of time.

Finally, let’s go together.

Finally, I am sorry to promote the component library I wrote based on the Taro framework: MP-Colorui.

I will be very happy if I can star easily. Thank you.

Click here for documentation

Click here to get the GitHUb address