FormValidation v0.8.1 is released, supports Bootstrap 4 alpha 3

Validating credit card expiration date

Examples

FormValidation provides some built-in validators to validate a credit card information which include

The question is that how to validate the expiration date which is often a required part in a payment form. Because the date validator asks for all the year, month and day, it isn't helpful in that case.

This example introduces some approaches to solve this problem.

For demonstrating purpose, the expiration date is treated as valid if it is in the range of current month and next 10 year

Using two separated fields

Assume that you are using two separated fields for expiration date. The first field asks user to fill the expiration month, and the second one is for the expiration year.

We will use the callback validator to check the validity of month and year fields. Because these fields depend on each other, the updateStatus() method is used to set month/year field as valid one.

The example uses the row option to indicate the field container when using multiple fields in the same row
<form id="paymentForm" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Expiration</label>
        <div class="col-xs-3">
            <input type="text" class="form-control" placeholder="Month" name="expMonth" />
        </div>
        <div class="col-xs-3">
            <input type="text" class="form-control" placeholder="Year" name="expYear" />
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#paymentForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            expMonth: {
                row: '.col-xs-3',
                validators: {
                    notEmpty: {
                        message: 'The expiration month is required'
                    },
                    digits: {
                        message: 'The expiration month can contain digits only'
                    },
                    callback: {
                        message: 'Expired',
                        callback: function(value, validator, $field) {
                            value = parseInt(value, 10);
                            var year         = validator.getFieldElements('expYear').val(),
                                currentMonth = new Date().getMonth() + 1,
                                currentYear  = new Date().getFullYear();
                            if (value < 0 || value > 12) {
                                return false;
                            }
                            if (year == '') {
                                return true;
                            }
                            year = parseInt(year, 10);
                            if (year > currentYear || (year == currentYear && value >= currentMonth)) {
                                validator.updateStatus('expYear', 'VALID');
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                }
            },
            expYear: {
                row: '.col-xs-3',
                validators: {
                    notEmpty: {
                        message: 'The expiration year is required'
                    },
                    digits: {
                        message: 'The expiration year can contain digits only'
                    },
                    callback: {
                        message: 'Expired',
                        callback: function(value, validator, $field) {
                            value = parseInt(value, 10);
                            var month        = validator.getFieldElements('expMonth').val(),
                                currentMonth = new Date().getMonth() + 1,
                                currentYear  = new Date().getFullYear();
                            if (value < currentYear || value > currentYear + 10) {
                                return false;
                            }
                            if (month == '') {
                                return false;
                            }
                            month = parseInt(month, 10);
                            if (value > currentYear || (value == currentYear && month >= currentMonth)) {
                                validator.updateStatus('expMonth', 'VALID');
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                }
            }
        }
    });
});
</script>

Supporting two digits year

The example above requires the expiration year to be a full four digits year. What if we need to support two digits year such as 18 instead of 2018?

The transformer option will be used in this case. The idea is that using this option to transform a two digits year number into a four digits year number:

$('#paymentForm').formValidation({
    framework: 'bootstrap',
    icon: {
        ...
    },
    fields: {
        expYear: {
            row: '.col-xs-3',
            transformer: function($field, validatorName, validator) {
                var year = parseInt($field.val(), 10);
                if (isNaN(year)) {
                    return year;
                } else {
                    // Add 2000 to year
                    return 2000 + year;
                }
            },
            validators: {
                ...
            }
        }
    }
});
<form id="paymentForm" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Expiration</label>
        <div class="col-xs-3">
            <input type="text" class="form-control" placeholder="Month" name="expMonth" />
        </div>
        <div class="col-xs-3">
            <input type="text" class="form-control" placeholder="Year" name="expYear" />
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#paymentForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            expMonth: {
                row: '.col-xs-3',
                validators: {
                    notEmpty: {
                        message: 'The expiration month is required'
                    },
                    digits: {
                        message: 'The expiration month can contain digits only'
                    },
                    callback: {
                        message: 'Expired',
                        callback: function(value, validator, $field) {
                            value = parseInt(value, 10);
                            var year         = validator.getFieldElements('expYear').val(),
                                currentMonth = new Date().getMonth() + 1,
                                currentYear  = new Date().getFullYear();
                            if (value < 0 || value > 12) {
                                return false;
                            }
                            if (year == '') {
                                return true;
                            }
                            year = parseInt(year, 10);
                            if (year > currentYear || (year == currentYear && value >= currentMonth)) {
                                validator.updateStatus('expYear', 'VALID');
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                }
            },
            expYear: {
                row: '.col-xs-3',
                transformer: function($field, validatorName, validator) {
                    var year = parseInt($field.val(), 10);
                    if (isNaN(year)) {
                        return year;
                    } else {
                        // Add 2000 to year
                        return 2000 + year;
                    }
                },
                validators: {
                    notEmpty: {
                        message: 'The expiration year is required'
                    },
                    digits: {
                        message: 'The expiration year can contain digits only'
                    },
                    callback: {
                        message: 'Expired',
                        callback: function(value, validator, $field) {
                            value = parseInt(value, 10);
                            var month        = validator.getFieldElements('expMonth').val(),
                                currentMonth = new Date().getMonth() + 1,
                                currentYear  = new Date().getFullYear();
                            if (value < currentYear || value > currentYear + 10) {
                                return false;
                            }
                            if (month == '') {
                                return false;
                            }
                            month = parseInt(month, 10);
                            if (value > currentYear || (value == currentYear && month >= currentMonth)) {
                                validator.updateStatus('expMonth', 'VALID');
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                }
            }
        }
    });
});
</script>

Using one field

The second approach is applied when using one field for both expiration month and year. The valid format of the field is YYYY/MM.

We will use the regexp validator to determine if the input follows the required format first, and the callback validator to check whether or not it is expired by comparing with current date.

<form id="paymentForm" class="form-horizontal">
    <div class="form-group">
        <label class="col-xs-3 control-label">Expiration date</label>
        <div class="col-xs-4">
            <input type="text" class="form-control" placeholder="YYYY/MM" name="expDate" />
        </div>
    </div>
</form>

<script>
$(document).ready(function() {
    $('#paymentForm').formValidation({
        framework: 'bootstrap',
        icon: {
            valid: 'glyphicon glyphicon-ok',
            invalid: 'glyphicon glyphicon-remove',
            validating: 'glyphicon glyphicon-refresh'
        },
        fields: {
            expDate: {
                verbose: false,
                validators: {
                    notEmpty: {
                        message: 'The expiration date is required'
                    },
                    regexp: {
                        message: 'The expiration date must be YYYY/MM',
                        regexp: /^\d{4}\/\d{1,2}$/
                    },
                    callback: {
                        message: 'The expiration date is expired',
                        callback: function(value, validator, $field) {
                            var sections = value.split('/');
                            if (sections.length !== 2) {
                                return {
                                    valid: false,
                                    message: 'The expiration date is not valid'
                                };
                            }

                            var year         = parseInt(sections[0], 10),
                                month        = parseInt(sections[1], 10),
                                currentMonth = new Date().getMonth() + 1,
                                currentYear  = new Date().getFullYear();

                            if (month <= 0 || month > 12 || year > currentYear + 10) {
                                return {
                                    valid: false,
                                    message: 'The expiration date is not valid'
                                };
                            }

                            if (year < currentYear || (year == currentYear && month < currentMonth)) {
                                // The date is expired
                                return {
                                    valid: false,
                                    message: 'The expiration date is expired'
                                };
                            }

                            return true;
                        }
                    }
                }
            }
        }
    });
});
</script>