Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery validator and a custom rule that uses AJAX

People also ask

Can you use jQuery with Ajax?

jQuery provides several methods for AJAX functionality. With the jQuery AJAX methods, you can request text, HTML, XML, or JSON from a remote server using both HTTP Get and HTTP Post - And you can load the external data directly into the selected HTML elements of your web page!

How use jQuery remote validation?

rules: { username: { minlength: 6, maxlength: 12, remote: { url: 'register/isUsernameAvailable', dataType: 'post', data: { 'username': $('#username'). val() }, success: function(data) { if (data. username == 'found') { message: { username: 'The username is already in use!

What is validator in jQuery?

Validation in JQuery: Using JQuery, a form is validated on the client-side before it is submitted to the server, hence saves the time and reduce the load on the server. Form Validation means to validate or check whether all the values are filled correctly or not.

What is validation in Ajax?

With Ajax, the data added to the form can be dynamically validated as the data is added to form fields using business logic in a server application. Thus, a complete form does not have to be posted to the server to check if data in the form is valid.


For anyone else who stumbles upon this, validate supports 'remote' method, which may not have existed in 2010:

https://jqueryvalidation.org/remote-method/

$("#myform").validate({
  rules: {
    email: {
      required: true,
      email: true,
      remote: {
        url: "check-email.php",
        type: "post",
        data: {
          username: function() {
            return $("#username").val();
          }
        }
      }
    }
  }
});

You are doing an AJAX request, ergo: the validation is already finished working when your custom validator returns either true or false.

You will need to work with async. See also this post: How can I get jQuery to perform a synchronous, rather than asynchronous, Ajax request?

Something like:

function myValidator() {
   var isSuccess = false;

   $.ajax({ url: "", 
            data: {}, 
            async: false, 
            success: 
                function(msg) { isSuccess = msg === "true" ? true : false }
          });
    return isSuccess;
}

Warning:

As of jQuery 1.8, the use of async: false with jqXHR ($.Deferred) is deprecated; you must use the success/error/complete callback options instead of the corresponding methods of the jqXHR object such as jqXHR.done() or the deprecated jqXHR.success().


It took me forever to figure out how to get a jsonified string containing the value of an element in the page into the remote request- this is the result of a combination of many hours and trying many search results.

Key points:

  1. async:false has been deprecated,
  2. the function call right after remote: is the key for creating the data string with the element's value. Trying to access a current value from the form after data: was returning a blank value for the field with dataType set as json.

        $("#EmailAddress").rules("add", {
        required: true,
        remote: function () { // the function allows the creation of the data string 
                              // outside of the remote call itself, which would not 
                              // return a current value from the form.
            var emailData = "{'address':'" + 
                            $('#SignupForm :input[id$="EmailAddress"]').val() + 
                            "'}";
            var r = {
                url: "foobar.aspx/IsEmailAvailable",
                type: "post",
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                cache: false,
                data: emailData,
                dataFilter: function(response) {
                    this.email_run = true; //fix for race condition with next button
                    return isAvailable(data); //return true or false
                }
            };
            return r;
        },
        messages: {
            remote: "* Email in use"
        }
    });
    

.ASPX page:

<input id="EmailAddress" required name="Email" type="email" placeholder="Email Address*" runat="server"/>

C# Code Behind:

[WebMethod]
    public static object IsEmailAvailable(string address){...}

Formatting the response object:

function isAvailable(data) {
    var response = JSON.parse(getJsonObject(data));
    return (response.isAvailable === "True") ? true : false;
};

//transform response string to a JavaScript Object()
//http://encosia.com/never-worry-about-asp-net-ajaxs-d-again/ 
function getJsonObject(data) {
    var msg = eval('(' + data + ')');
    if (msg.hasOwnProperty('d'))
        return msg.d;
    else
        return msg;
};

Here's my "old school" hack...

Below a utility function that allows the use of "asynchronous" validations with "jquery.validate.js" library. This function creates a delay between user keystrokes otherwise the validation function "validFunc" will be called "all time" which is not very performative in some circumstances and especially problematic for functions that perform validations on "serverside"/"backend" (ajax calls basically). In this way the "validFunc" validation function is only called when the user stops typing for a certain period of time which also allows a "realtime" validation ("onkeyup": true on jqv settings) as it occurs while the user is typing.

IMPORTANT: Validations involving the use of the "jqvAsyncValid" function should always be the last ones to avoid conflicts with the others due to the asynchrony.

{
    [...]
    "rules": {
        "field_name": {
            "required": true,
            "maxlength": 12,
            "minlength": 4,

            // NOTE: Validation involving the use of the "jqvAsyncValid" function. By Questor
            "my_custom_ajax_validation": true

        },
    [...]
}

ANSWER'S CODE:

// NOTE: Adds the custom validation "my_custom_ajax_validation". By Questor
$.validator.addMethod("my_custom_ajax_validation", function (value, element) {
    return jqvAsyncValid(element, "my_custom_ajax_validation", myValidationFunc, this);
}, "My error message!");

// NOTE: My validation function. By Questor
function myValidationFunc(domElement) {
    if (someFuncWithAjaxCall(domElement.value) == "ALL_RIGHT!") {
        return true;
    } else {
        return false;
    }
}

// NOTE: Global "json" variable that stores the "status" ("isValid") and cycle control
// ("toCtrl") of asynchronously validated elements using the "jqvAsyncValid" function.
// By Questor
var jqvAsyncVState = {};

// NOTE: A utility function that allows the use of asynchronous validations with
// "jquery.validate.js". This function creates a delay between one user keystroke and
// another otherwise the validation function "validFunc" will be called "all time"
// which is not very performative in some circumstances and especially problematic
// for functions that perform validations on the serverside/backend (ajax calls basically).
// In this way the "validFunc" validation function is only called when the user stops
// typing for a certain period of time, which also allows a "realtime" validation
// as it occurs while the user is typing. By Questor
// [Ref .: https://jqueryvalidation.org/ ]
//. domElement - DOM element informed by jqv in the "addMethod" for the anonymous
// function;
//. asyncRuleNm - Validation name added via "addMethod";
//. validFunc - Function that will do the validation. Must have the signature
// "funcName(domElement)" returning "true" for valid and "false" for not;
//. jQValidInst - Instance of the current jqv within "addMethod". It is usually
// denoted by "this";
//. toInMsecs - "Timeout" in "milliseconds". If not informed the default will be
// 1500 milliseconds. Be careful not to use a very short timeout especially in
// "ajax" calls so as not to overload the serverside/backend.
// Eg.: `return jqvAsyncValid(element, "my_custom_ajax_validation", myValidationFunc, this);`.
function jqvAsyncValid(domElement, asyncRuleNm, validFunc, jQValidInst, toInMsecs) {
    if (typeof toInMsecs === "undefined" || toInMsecs === "") {
        toInMsecs = 1500;
    }
    var domEKey = jQValidInst.currentForm.id + domElement.name;

    // NOTE: The validation messages need to be "displayed" and "hidden" manually
    // as they are displayed asynchronously. By Questor
    function errMsgHandler() {
        if (jqvAsyncVState[domEKey]["isValid"]) {

            // NOTE: If the current error message displayed on the element was that
            // set in the rule added via "addMethod" then it should be removed since
            // the element is valid. By Questor
            // [Ref.: https://stackoverflow.com/a/11652922/3223785 ,
            // https://stackoverflow.com/a/11952571/3223785 ]
            if (jQValidInst.errorMap[domElement.name] == $.validator.messages[asyncRuleNm]) {
                var iMsgNow = {};
                iMsgNow[domElement.name] = "";
                jQValidInst.showErrors(iMsgNow);
            }

        } else {
            var iMsgNow = {};

            // NOTE: If the element is invalid, get the message set by "addMethod"
            // for current rule in "$.validator.messages" and show it. By Questor
            iMsgNow[domElement.name] = $.validator.messages[asyncRuleNm];
            jQValidInst.showErrors(iMsgNow);

        }
    }
    if (!jqvAsyncVState.hasOwnProperty(domEKey)) {

        // NOTE: Set the global json variable "jqvAsyncVState" the control attributes
        // for the element to be asynchronously validated if it has not already been
        // set. The key "domEKey" is formed by the "id" of the "form" that contains
        // the element and the element's "name". By Questor
        jqvAsyncVState[domEKey] = {
            "toCtrl": null,
            "isValid": undefined
        };

    }
    var useOnKeyup = true;

    // NOTE: The "onblur" event is required for "first validation" that only occurs
    // in a "blur" event - this is inherent to jqv - and for situations where the
    // user types very fast and triggers "tab" and the event "onkeyup" can not deal
    // with it. By Questor
    domElement.onblur = function (e) {
        jqvAsyncVState[domEKey]["isValid"] = validFunc(domElement);
        errMsgHandler();
        useOnKeyup = false;
    }
    if (useOnKeyup) {

        // NOTE: The strategy with the event "onkeyup" below was created to create
        // a "delay" between a "keystroke" and another one otherwise the validation
        // function "validFunc" will be called "all time" which is not very performative
        // in some circumstances and especially problematic for functions that perform
        // serverside/backend validations (ajax calls basically). In this way the
        // "validFunc" validation function is only called when the user stops typing
        // for a certain period of time ("toInMsecs"). By Questor
        domElement.onkeyup = function (e) {

            // NOTE: Clear the "toCtrl" if it has already been set. This will
            // prevent the previous task from executing if it has been less than
            // "toInMsecs". By Questor
            clearTimeout(jqvAsyncVState[domEKey]["toCtrl"]);

            // NOTE: Make a new "toCtrl" set to go off in "toInMsecs" ms. By Questor
            jqvAsyncVState[domEKey]["toCtrl"] = setTimeout(function () {
                jqvAsyncVState[domEKey]["isValid"] = validFunc(domElement);
                errMsgHandler();
            }, toInMsecs);
        };
    }
    return jqvAsyncVState[domEKey]["isValid"];
}