Just join the what to what?
This is one of those posts that I’ve written more the benefit of Future Me than anyone else. Future Me is a forgetful fellow, it seems, so this will hopefully save him some wretched googling while trying to recall that one crucial detail that makes this just work. If it helps you in a similar way then that’s nice.
First of all, this assumes that you already have Unobtrusive Javascript set up and working in your project. There are plenty of guides on that and I’m confident even Future Me can handle it. It also assumes you’re already familiar with creating custom validation attributes. What it does concern is writing a jQuery adapter to do the validation client-side. There are plenty of guides on this, but what I’m presenting here is the bare minimum required for simple validators.
Let’s write a tiny amount of code
The first thing to do is modify your existing validation attribute class. This is done on in two places:
The class itself needs to inherit IClientValidatable:
public class MyLovelyCustomValidator : ValidationAttribute, IClientValidatable
And the GetClientValidationRules class from that interface needs implementing:
public IEnumerableGetClientValidationRules(ModelMetadata metadata, ControllerContext context) { var rule = new ModelClientValidationRule(); rule.ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()); rule.ValidationType = "myadaptername"; yield return rule; }
The FormatErrorMessage return being added to the rule above comes from the ValidationAttribute your custom validator inherits. You may have overridden it, you may not. That’s unimportant here.
More important is the ValidationType being added. I’ve set it to myadaptername in this example because it will be used as the name of your jQuery adapter too.
Now we’ll write the adapter. Create a new .js file somewhere sensible in your project and add this:
$.validator.unobtrusive.adapters.addBool("myadaptername");
There are a number of methods available for adding adapters. There’s a really good summary of them (and a more in-depth examination of custom validation) at Brad Wilson’s excellent if now slightly ancient blog post.
I’ve chosen addBool(adapterName, [ruleName]) because this is very simple validation based solely on the input value, which is what this validator does. Note that it has an optional ruleName parameter which we’re not using. The rule name could be one of the built-in jQuery validation rules (eg mandatory) or it could be one of your own. If it’s omitted the adapterName is used instead. Since I’m writing my own specifically for this adapter, I’ve omitted it for simplicity’s sake.
Next we add the validator to the same file:
$.validator.addMethod("myadaptername", function(value) { return value > 0; //your implementation here });
Again this is very simple. We pass in the adapter name, and a function to perform the validation. The full signature for the function is function (value, element, params), but as we don’t need element (our validator is only testing the value itself) and we have no additional parameters, we can omit them.
Finally, do your validation within the function. Include the .js file in the page containing your form, and assuming validation was working fine before, it should now validate the field using your custom validator client-side too.
As an afterword, the reason this example is so simple is because I have a validator that tests whether a nullable decimal has a value greater than zero. However hopefully you, or Future Me, will find it a useful basis to build more complex validators from.