Deep Dive into Validation Rules

Implementation of validation rules has already been covered in one of my previous posts, but being implemented in 5 different ways caught me a bit off guard! ๐Ÿ˜€

Recently, I was working on a task which included implementation of validation rules on an input field. On checkout, where else? ๐Ÿ˜€ While I was in the process of researching what is where and how to complete the task, I have stumbled upon the following five levels of how validation rules are parsed in the validation phase. Here is a snippet of how it looks like in /lib/web/jquery/jquery.validate.js:

var data;
...
data = $.validator.normalizeRules(
    $.extend(
        {},
        $.validator.metadataRules(element),
        $.validator.classRules(element),
        $.validator.attributeRules(element),
        $.validator.dataRules(element),
        $.validator.staticRules(element)
    ), 
element );

Let’s briefly cover each entry and demonstrate what is the connection when used in markup. In order to clearly see the differences in implementation, I have used different validation rules on purpose.

Metadata Rules

Metadata validation rules are defined via data-validate attribute (phoneUS):

<input id="field1" class="input-text" name="field1" type="text" value="phoneUS" data-validate="{ phoneUS: true }" />

Class Rules

Class-based validation rules are defined via class attribute (time12h):

<input id="field2" class="input-text time12h" name="field2" type="text" value="time12h" />

Attribute Rules

Attribute-based validation rules are defined as rule name as attribute and “true” as its value (ipv4):

<input id="field3" class="input-text" name="field3" type="text" value="ipv4" ipv4="true" />

Data Rules

Data-based validation rules are defined as any “data” attribute, followed by the “rule” word and the actual rule ID, like the following (ipv6):

<input id="field4" class="input-text" name="field4" type="text" value="data-rule-ipv6" data-rule-ipv6="" />

Static Rules

Static-based validation rules are defined as part of “data-mage-init” attribute, which is defined on the form itself (alphanumeric):

<form data-mage-init="{"validation":{ "rules": { "field5": { "alphanumeric": true } } }}">...</form>

Let’s see all of those rules in action. Here is the form with all of the fields on page load:

Input fields on page load

After submitting and validation, here is how the form looks like now:

Input fields after validation

As you can see on the screenshot, the validation rules are being applied and triggered correctly. However, there is a bug when parsing the attribute rules.

Parsing issue - value is string, not boolean

We all know how problematic variable types can be. Let’s create a mixin for this issue and fix it!

Register Your Mixin

Add a new entry in requirejs-config.js in your theme and add the following code:

var config = {
    config: {
        mixins: {
            'jquery/jquery.validate': {
                'jquery/jquery.validate-mixin': true
            }
        }
    },
};

Create the Mixin

Create the file jquery.validate-mixin.js in the correct folder and add the following code:

define([
    'mage/utils/wrapper',
    'jquery'
], function (wrapper, $) {
    'use strict';
 
    return function () {
        $.fn.validate = wrapper.wrapSuper($.fn.validate, function (options) {
            this._super(options);
 
            $.validator.attributeRules = wrapper.wrapSuper($.validator.attributeRules, function (element) {
                var rules = {},
                $element = $( element ),
                type = element.getAttribute( "type" ),
                method, value;
 
                for ( method in $.validator.methods ) {
 
                    // Support for <input required="" type="text" /> in both html5 and older browsers
                    if ( method === "required" ) {
                        value = element.getAttribute( method );
 
                        // Some browsers return an empty string for the required attribute
                        // and non-HTML5 browsers might have required="" markup
                        if ( value === "" ) {
                            value = true;
                        }
 
                        // Force non-HTML5 browsers to return bool
                        value = !!value;
                    } else {
                        value = $element.attr( method );
 
                        // CONVERT STRING TO BOOLEAN
                        if (value === 'true') {
                            value = true
                        }
                    }
 
                    this.normalizeAttributeRule( rules, type, method, value );
                }
 
                // 'maxlength' may be returned as -1, 2147483647 ( IE ) and 524288 ( safari ) for text inputs
                if ( rules.maxlength && /-1|2147483647|524288/.test( rules.maxlength ) ) {
                    delete rules.maxlength;
                }
 
                return rules;
            });
        })
 
        return $
    };
})

With the correct implementation of the mixin, the value is now properly rendered as boolean value.

And that’s it, as simple as that! ๐Ÿ˜€

Maaaaagic!

Thanks for reading my article, hope you liked it! If you have any questions, feel free to leave a comment or contact us. We’d be glad to help!

Until next time! ๐Ÿ™‚