After discussing the example in the previous article with a few friends, I realized the example was incomplete. The example works well as written, but only because jquery.validate and jquery.validate.unobtrusive already contain a ValidationType of "creditcard. Therefore, no additional client-side validation had to be added for the example. However, for a complete example of MVC3 custom validation attributes for client and server side validation with unobtrusive ajax, we need to add our custom jquery validators. In this example, we will create a custom MVC3 attribute to test whether a model property equals a specific value and add the custom jquery validators for client-side validation.
Extending the previous example, we will add the validation attribute MustNotEqualAttribute. The logic is straight forward, and tests whether the property is equal or not, depending on the condition to the value specified. What is new here is the ModelClientValidationRule, ModelClientValidationMustNotEqualRule. This rule defines how jquery.unobtrusive will handle the validation - ValidationType is the method name and the parameter specify values used in the validation. These values are recorded in the fields data attributes:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Web.Mvc;
namespace AjaxTest.Validation
{
public enum Condition
{
NotEqualTo,
EqualTo
}
[AttributeUsage(AttributeTargets.Property)]
public class MustBeAttribute : ValidationAttribute, IClientValidatable
{
private object _valueToCompare { get; set; }
private readonly Condition _condition = Condition.EqualTo;
public MustBeAttribute(Condition condition, object value)
{
_condition = condition;
_valueToCompare = value;
}
public override bool IsValid(object value)
{
try
{
switch (_condition)
{
case Condition.EqualTo:
return value.Equals(_valueToCompare);
case Condition.NotEqualTo:
return !value.Equals(_valueToCompare);
default:
return false;
}
}
catch (Exception)
{
return false;
}
}
public class ModelClientValidationMustBeRule : ModelClientValidationRule
{
public ModelClientValidationMustBeRule(string errorMessage, Condition condition, object propertyValue)
: base()
{
this.ErrorMessage = errorMessage;
this.ValidationType = "mustbe";
this.ValidationParameters.Add("propertyvalue", propertyValue);
this.ValidationParameters.Add("condition", (int)condition);
}
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
yield return new ModelClientValidationMustBeRule(ErrorMessage, _condition, _valueToCompare);
}
}
}
Next, we add the attribute to one of the models properties. In this example, we specify that the prooperty UserName cannot be equal to the value 'abcd':
[Required]
[MustBe(Condition.NotEqualTo, "abcd", ErrorMessage = "Username must not equal 'abcd'.")]
[Display(Name = "User name")]
public string UserName { get; set; }
The render html for the input field now looks like the following. Note the values in the attributes data-val-mustbe-condition and data-val-mustbe-propertyvalue. These values match what we specified in the attribute declaration and are parsed by jquery.unobtrusive for client-side validation:
<input data-val="true" data-val-mustbe="Username must not equal 'abcd'." data-val-mustbe-condition="0" data-val-mustbe-propertyvalue="abcd" data-val-required="The User name field is required." id="UserName" name="UserName" type="text" value="">
The client-side validation is accomplished with the following javascript. The first function creates the 'mustbe' validator which accomplishes the validation logic, the second function is a helper function for unobtrusive to use to pass values to the validator, and the third function adds the unobtrusive 'mustbe' adapter.
(function () {
jQuery.validator.addMethod('mustbe', function (value, element, params) {
var testValue = params['propertyvalue'];
var condition = params['condition'];
if ((condition == '0') && (value != testValue))
return true;
if ((condition == '1') && (value == testValue))
return true;
return false;
});
var setValidationValues = function (options, ruleName, value) {
options.rules[ruleName] = value;
if (options.message) {
options.messages[ruleName] = options.message;
}
};
var $Unob = $.validator.unobtrusive;
$Unob.adapters.add("mustbe", ["propertyvalue", "condition"], function (options) {
var value = {
propertyvalue: options.params.propertyvalue,
condition: options.params.condition
};
setValidationValues(options, "mustbe", value);
});
})();
That's it! Now you have a complete example to create your own custom MVC validation attribute with unobtrusive client-side validation. The full source is attached.
Cheers and happy coding - Don!
AjaxTest.zip (7.08 mb)