Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / form

AJAX Form using a jQuery Extension Method

4.50/5 (2 votes)
7 Aug 2012CPOL3 min read 25.6K  
A little guide to introduce you how to create a JQuery Extension Method

Introduction

In this tip I’m going to simplify a process I find myself doing over and over by using a jQuery Extension method.

To begin let’s review a block of code I found my self writing over and over in various parts of my web applications.

The Hard Way

JavaScript
<script type="text/javascript">
    $(function () {
        $("#myForm").submit(function (e) {
            e.preventDefault();
            var postData = $("#myForm").serialize();
            var action = $("#myForm").attr("action");
            $.post(action, function (data) {
                $("#PostResult").html(data);
            });
        });
    });
</script>

<form id="myForm" action="someservice.ashx">
    <input type="text" name="Field1" /><br />
    <input type="text" name="Field2" /><br />
    <input type="text" name="Field3" /><br />

    <input type="submit"/>
</form>

<div id="PostResult"></div>  

Essentially I’m taking a form and rather than doing a traditional POST, I’m serializing the form and posting it using the JQuery $.Post() function.

This works fine – but having to do this every time you want to AJAXify a form is pain.

The Better Way – Using a JQuery Extension

So let’s refractor this into an extension method so it’s reusable. To begin let’s create a bare extension method.

JavaScript
$.fn.ajaxform = function(options) {
    return this.each(function () {
    });
};

The $.fn.ajaxform is the name of your method so now we will be able to call it like so : $(“#myForm”).ajaxform(); 

if you don’t follow the return this.each pattern (which is not required) your method will not be chain-able.  That means it will always have to come at the end of the chain.  So if you want that then omit that section, however it’s highly recommended to maintain the chain.

Next were going to build up our option stack and set some default values. 

JavaScript
$.fn.ajaxform = function (options) {
            
            var settings = $.extend({
        'postData': $(this).serialize(),
        'action': $(this).attr("action"),
        'onComplete': function (data) {}
    }, options);

    return this.each(function () {
    });
};

see how we just brought our variables we used up above and made them into options for method.  Now when using our method you can either use the default functionality – or override as needed.

Now lets add our functionality.

JavaScript
$.fn.ajaxform = function (options) {
    
    var settings = $.extend({
        'postData': $(this).serialize(),
        'action': $(this).attr("action"),
        'onComplete': function (data) {}
    }, options);

    return this.each(function () {
                $(this).submit(function (e) {
            e.preventDefault();
            $.post(settings.action, settings.postData, settings.onComplete);
        });
    });
};

And now we can replace our original script to use our method like this:

JavaScript
$(function () {
    $("#myForm").ajaxform({
        onComplete: function (data) {
            $("#PostResult").html(data);
        }
    });
});

That’s much easier on the eyes considering we will probably be using it a lot. 

We have now completed the refactor of the original code, but to complete the AJAXForm extension I want to add the following functionality:

  • Ability to check for required fields,
  • Mark these fields as required
  • Display an error if not completed, and of course not submit if omitted. 

Required Fields

Ok first lets do the required fields.

To do this were going to add in a setting option to pass in a string array with the names of the required fields. Create a function to perform the validation, and call this function prior to submitting our form. 

JavaScript
$.fn.ajaxform = function (options) {
    
    var settings = $.extend({
                'requiredFields': [],
        'errorMessageTarget': '', 
        ...
    }, options);

            var checkRequiredFields = function() {
        return {
            isValid: false,
            errorMessage: ''
        };
    };

    return this.each(function () {
        $(this).submit(function (e) {
            e.preventDefault();
                    var validationResult = checkRequiredFields();
            if (validationResult.isValid) {
                $.post(settings.action, settings.postData, settings.onComplete);
            }else {
                        $(settings.errorMessageTarget).html(validationResult.errorMessage);
            }
        });
        
    });
};

And now we need to write the code to check to ensure the form values are not null.

JavaScript
var checkRequiredFields = function(form) {
    var isValid = true;
    var errors = "";
    for (var i in settings.requiredFields) {
        var rField = $(form).find(settings.requiredFields[i]);
        if ($(rField).val() == "") {
            errors += "<li>" + $(rField).attr("name") + " is Required</li>";
            $(rField).addClass(settings.fieldErrorClass);
            isValid = false;
        } else {
            $(rField).removeClass(settings.fieldErrorClass);
        }
    }
    return {
        isValid: isValid,
        errorMessage: errors
    };
};

Next we want to add a required field class to each of the required form fields. to do this were going to add a little code into the return this.each(function () block 

JavaScript
return this.each(function () {
for (var i = 0; i < settings.requiredFields.length; i++) {
    $(this).find('*[name="' + settings.requiredFields[i] + '"]').addClass(settings.requiredFieldClass);
}
....

OK,so that completes our required field validation – here’s a post of the entire code to this point:

JavaScript
<script type="text/javascript">
    $(function () {
        $("#myForm").ajaxform({
            requiredFields: ["Field1", "Field2"],
            errorMessageTarget : "#PostResult",
            onComplete: function (data) {
                $("#PostResult").html(data);
            }
        });
    });
</script>

<form id="myForm" action="someservice.ashx">
    <input type="text" name="Field1" /><br />
    <input type="text" name="Field2" /><br />
    <input type="text" name="Field3" /><br />
    
    <input type="submit"/>
</form>

<div id="PostResult"></div>
    
<script type="text/javascript">
    $.fn.ajaxform = function (options) {
       
        var settings = $.extend({
            "requiredFields": [],
            "requiredFieldClass" : "field-required",
            "errorMessageTarget": "",
            "fieldErrorClass": "field-error",
            "postData": $(this).serialize(),
            "action": $(this).attr("action"),
            "onComplete": function (data) {}
        }, options);

        var checkRequiredFields = function(form) {
            var isValid = true;
            var errors = "";
            for (var i in settings.requiredFields) {
                var rField = $(form).find(settings.requiredFields[i]);
                if ($(rField).val() == "") {
                    errors += "<li>" + $(rField).attr("name") + " is Required</li>";
                    $(rField).addClass(settings.fieldErrorClass);
                    isValid = false;
                } else {
                    $(rField).removeClass(settings.fieldErrorClass);
                }
            }
            return {
                isValid: isValid,
                errorMessage: errors
            };
        };

        return this.each(function () {
            for (var i = 0; i < settings.requiredFields.length; i++) {
                $(this).find('*[name="' + settings.requiredFields[i] + '"]').addClass(settings.requiredFieldClass);
            }


            $(this).submit(function (e) {
                e.preventDefault();
                var validationResult = checkRequiredFields(this);
                if (validationResult.isValid) {
                    $.post(settings.action, settings.postData, settings.onComplete);
                }else {
                    $(settings.errorMessageTarget).html(validationResult.errorMessage);
                }
            });
            
        });
    };
</script>

Before Submit Function 

Our next Item on the agenda is to : Provide the ability to pass in a function that executes before the form is submitted (for additional validation). So that means we need a method that has the ability to append to our validation result. This means were going to need to extract the validation result into a variable and work against it in our new method.

JavaScript
var validationState = {
    isValid: true,
    errorMessage: ''
};

var checkRequiredFields = function(form) {
    for (var i in settings.requiredFields) {
        var rField = $(form).find('*[name="' + settings.requiredFields[i] + '"]');
        if ($(rField).val() == "") {
            validationState.errorMessage += 
               "<li>" + $(rField).attr("name") + " is Required</li>";
            $(rField).addClass(settings.fieldErrorClass);
            validationState.isValid = false;
        } else {
            $(rField).removeClass(settings.fieldErrorClass);
        }
    }
};

Now were going to add a method that takes in the validationState and is run before the required filed check

JavaScript
var settings = $.extend({
	....
    'beforeSubmit': function(valState) { },
	.....
    
}, options);
	...
    $(this).submit(function (e) {
        e.preventDefault();
        settings.beforeSubmit(validationState);
        checkRequiredFields(this);
       ....

And now we can leverage it in our initialization code like this:

JavaScript
$("#myForm").ajaxform({
        requiredFields: ['Field1', 'Field2'],
                beforeSubmit: function (validationState) {
            if ($('input[name="Field3"]').val() != "foobar") {
                validationState.isValid = false;
                validationState.errorMessage += '<li>Field3 Needs to be FooBar</li>';
            }
            
        },
        errorMessageTarget: '#PostResult',
        
        onComplete: function (data) {
            $("#PostResult").html(data);
        }
    });
});

And with that I Believe we have completed our requirements.

Final Code

JavaScript
<script type="text/javascript">
    $(function () {
        $("#myForm").ajaxform({
            requiredFields: ["Field1", "Field2"],
            beforeSubmit: function (validationState) {
                if ($('input[name="Field3"]').val() != "foobar") {
                    validationState.isValid = false;
                    validationState.errorMessage += '<li>Field3 Needs to be FooBar</li>';
                }
                
            },
            errorMessageTarget: '#PostResult',
            
            onComplete: function (data) {
                $("#PostResult").html(data);
            }
        });
    });
</script>

<form id="myForm" action="someservice.ashx">
    <input type="text" name="Field1" /><br />
    <input type="text" name="Field2" /><br />
    <input type="text" name="Field3" /><br />
    
    <input type="submit"/>
</form>

<div id="PostResult"></div>

<script type="text/javascript">
    $.fn.ajaxform = function (options) {
        
        var settings = $.extend({
            "requiredFields": [],
            "requiredFieldClass" : "field-required",
            "errorMessageTarget": "",
            "fieldErrorClass": "field-error",
            "beforeSubmit": function(valState) { },
            "postData": $(this).serialize(),
            "action": $(this).attr("action"),
            "onComplete": function(data) { }
            
        }, options);

        var validationState = {
            isValid: true,
            errorMessage: ''
        };

        var checkRequiredFields = function(form) {
            for (var i in settings.requiredFields) {
                var rField = $(form).find('*[name="' + settings.requiredFields[i] + '"]');
                if ($(rField).val() == "") {
                    validationState.errorMessage += "<li>" + $(rField).attr("name") + " is Required</li>";
                    $(rField).addClass(settings.fieldErrorClass);
                    validationState.isValid = false;
                } else {
                    $(rField).removeClass(settings.fieldErrorClass);
                }
            }
        };

        return this.each(function () {
            for (var i = 0; i < settings.requiredFields.length; i++) {
                $(this).find('*[name="' + settings.requiredFields[i] + '"]').addClass(settings.requiredFieldClass);
            }


            $(this).submit(function (e) {
                e.preventDefault();
                settings.beforeSubmit(validationState);
                checkRequiredFields(this);
                if (validationState.isValid) {
                    $.post(settings.action, settings.postData, settings.onComplete);
                }else {
                    $(settings.errorMessageTarget).html(validationState.errorMessage);
                }
            });
            
        });
    };
</script>

If you found this article helpfull you can find more like it at http://www.sympletech.com
This code is also part of the SympleLibJS Components found here: https://github.com/sympletech/SympleLib.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)