5

I have a senario where i have to check for a Username existance in database while doing a registration page. for this i have implemented a Remote Attribute for remote validation in my model

[Remote("CheckUserNameAvaliable", "User", Httpmethod="Post")]
public string Username {get; set;}

and my method looks like this

[HttpPost]
public JsonResult CheckUserNameAvaliable(string UserName)
{
    SessionUser currentUser = this.sessionHelper.GetCurrentUser();
    User user = this.userService.GetByUsername(UserName);
    if (user != null && user.Id != currentUser.Id)
    {
        return Json(false);
    }

    return Json(true);
}

It works fine for me, but the problem is that whenever we made a key up on the username Textbox it will file this remote validation, but according to my requirement i have to fire this remote validation only after entering value in the username Textbox. For that can we forcefully add a delay to the remote validation attribute?

Do anyone know how to add delay to remote validation in MVC3?

Febin J S
  • 1,358
  • 3
  • 26
  • 53

4 Answers4

4

MVC3 uses the jquery.validate plugin in the browser. With jquery.validate, field validation doesn't start until the first change and blur of the field, or upon submit if the field wasn't touched.

Once field validation starts for a field, the onkeyup setting of the validator controls whether validation is performed on each keyup event. If you're performing remote validation on a field with every key press it's probably sending too many ajax messages stressing both the client and server.

As the other responders mentioned, you can suppress the keyup validation by setting validator.settings.onkeyup = false, but that suppresses onkeyup validation on all form fields, not just those with a remote validation rule. What I suggest is that you suppress keyup while the field with remote validation has focus and then turn it back on when you blur the field.

Suppressing onkeyup is easy. The problem is that changing the setting from false to true does not turn the feature back on. To turn it back on you need to set it to the function defined in the defaults of the $.validator object.

Here's the javascript code to include on your page if you're using MVC unobtrusive validation. It will cover all forms on your page. And be sure to use jquery.validate version 1.9.0 or later 'cause earlier versions don't work properly with IE 7 or 8 (yeah, I have to support those users too):

    $("form input[data-val-remote-url]").on({
        focus: function () {
            $(this).closest('form').validate().settings.onkeyup = false;
        },
        blur: function () {
            $(this).closest('form').validate().settings.onkeyup =
                $.validator.defaults.onkeyup;
        }
    });
Lee Greco
  • 743
  • 2
  • 11
  • 23
2

I have been caught on similar problems before with MVC3 and remote validation. Guess you want the event to fire on blur but not sure how you'd tell the remote validation that..

Hopefully this will help

ASP.NET Remote Validation only on blur?

Community
  • 1
  • 1
DevDave
  • 6,700
  • 12
  • 65
  • 99
0

None of the approach was a suitable answer as it removes the ability to revalidate on keyup when the error is already displayed. I stll want a debounce, rather than the current implementation that hits the API on every keyup.

My solution was to override the remote method.

// Will use a simple variable to hold our current remote API request
// may want to do something smarter later
var holderForCurrentRemoteCall = null;

// keep the original as we're going to make use of it
jQuery.validator.methods._remoteRule = jQuery.validator.methods.remote;

jQuery.validator.methods.remote = function (value, element, param, method) {
    // the orig remote method sets a pending class when the call is in progress
    // we'll quickly exit and continue the pending.
    if ($(element).hasClass('pending')) { return 'pending'; }

    // we're going to need the previous value to stop any
    // mid-stream request
    method = typeof method === "string" && method || "remote";
    var previous = this.previousValue(element, method);

    if (holderForCurrentRemoteCall != null) {
        // stop any current request
        this.stopRequest(element, previous.valid);
        // and clear out any timeout that hasn't executed yet
        clearTimeout(holderForCurrentRemoteCall);
        holderForCurrentRemoteCall  = null;
    }

    // Within the orig remote call, it has some mnemonic so it won't
    // make the same request twice
    // as we can't capture it here, bubbling the request to orig would
    // not give a desirable outcome for our stub. 
    // so I've taken the mnemonic part of the code and replicate here
    if (this.optional(element)) {
        return "dependency-mismatch";
    }

    if (!this.settings.messages[element.name]) {
        this.settings.messages[element.name] = {};
    }
    previous.originalMessage = previous.originalMessage || this.settings.messages[element.name][method];
    this.settings.messages[element.name][method] = previous.message;

    param = typeof param === "string" && { url: param } || param;
    var optionDataString = $.param($.extend({ data: value }, param.data));
    if (previous.old === optionDataString) {
        return previous.valid;
    }
    // End copy-pasta code

    holderForCurrentRemoteCall  = setTimeout(() => {
        jQuery.validator.methods._remoteRule.call(this, value, element, param, method);
        // remove the holder as we have nothing to cancel
        holderForCurrentRemoteCall = null;
    }, 300);

    return "pending";
}
jonyeezs
  • 41
  • 1
  • 7
0

If you don't want to validate on keyup but on blur then this is what you want

Community
  • 1
  • 1
Alexandros B
  • 1,881
  • 1
  • 23
  • 36
  • Answer is good, but the problem is that it is setting globally, but if want to specify it in a specific page only then its quite odd i think. – Febin J S Nov 18 '11 at 04:06