Reputation: 3820
I'm currently building a MVC4 project which uses unobtrusive validation through data annotations.
I have a specific field, "PostalCode", which is both [Required] and has a [RegularExpression] validator attached to it. The thing is, this regular expression should only be verified if a specific country is selected. (This country will be the default value and we can assume in nearly all cases this will be used)
Now I need some way to disable this regex validation when a different country is selected, while keeping the required validator active.
Nearly all sollutions I've found are using a jQuery.validator.defaults.ignore filter, but this would disable both validators on that item.
Any thoughts on how to best tackle this problem?
Edit: Small code snippet to show how this is working.
[Required]
[RegularExpression("^[1-9]\\d{3} ?[a-zA-Z]{2}$"] //Should only be verified if Country == "The Netherlands"
string PostalCode{get;set;}
[Required]
string Country {get;set;}
Upvotes: 2
Views: 1646
Reputation: 3820
In the end I made up my own ValidationAttribute based on this blog post: http://thewayofcode.wordpress.com/2012/01/18/custom-unobtrusive-jquery-validation-with-data-annotations-in-mvc-3/ It's an elegant sollution and required way less work than I anticipated.
Edit: As per request, I provide the sollution created by myself:
// DependentRegularExpressionAttribute.cs
/// <summary>
/// Only performs a regular expression validation if a specified other property meets a validation requirement
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DependentRegularExpressionAttribute : ValidationAttribute, IClientValidatable
{
private readonly Regex _regex;
private readonly string _otherPropertyName;
private readonly Regex _otherPropertyRegex;
public DependentRegularExpressionAttribute(string regex, string otherPropertyName, string otherPropertyRegex)
{
_regex = new Regex(regex);
_otherPropertyName = otherPropertyName;
_otherPropertyRegex = new Regex(otherPropertyRegex);
}
/// <summary>
/// Format the error message filling in the name of the property to validate and the reference one.
/// </summary>
/// <param name="name">The name of the property to validate</param>
/// <returns>The formatted error message</returns>
public override string FormatErrorMessage(string name)
{
return string.Format(ErrorMessageString, name, _regex, _otherPropertyName, _otherPropertyRegex);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var validationResult = ValidationResult.Success;
if (value == null || String.IsNullOrEmpty(value as string))
return validationResult;
// Using reflection we can get a reference to the other property
var otherPropertyInfo = validationContext.ObjectType.GetProperty(_otherPropertyName);
var otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || String.IsNullOrEmpty(otherPropertyValue as string))
return validationResult;
if (_otherPropertyRegex.IsMatch(otherPropertyValue.ToString()))
{
if (!_regex.IsMatch(value.ToString()))
validationResult = new ValidationResult(ErrorMessage);
}
return validationResult;
}
#region IClientValidatable Members
/// <summary>
///
/// </summary>
/// <param name="metadata"></param>
/// <param name="context"></param>
/// <returns></returns>
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
string errorMessage = FormatErrorMessage(metadata.DisplayName ?? metadata.PropertyName);
// The value we set here are needed by the jQuery adapter
var dependentRegexRule = new ModelClientValidationRule
{
ErrorMessage = errorMessage,
ValidationType = "dependentregex"
};
//"otherpropertyname" is the name of the jQuery parameter for the adapter, must be LOWERCASE!
dependentRegexRule.ValidationParameters.Add("otherpropertyname", _otherPropertyName);
dependentRegexRule.ValidationParameters.Add("regex", _regex);
dependentRegexRule.ValidationParameters.Add("otherpropertyregex", _otherPropertyRegex);
yield return dependentRegexRule;
}
#endregion
}
// customvalidation.js
$.validator.addMethod("dependentregex", function (value, element, params) {
var regex = new RegExp(params[0]);
var otherElement = document.getElementById(params[1]);
var otherElementRegex = new RegExp(params[2]);
if (!value || !otherElement.value)
return true;
if (otherElementRegex.test(otherElement.value)) {
if (!regex.test(element.value))
return false;
}
return true;
});
$.validator.unobtrusive.adapters.add("dependentregex", ["regex", "otherpropertyname", "otherpropertyregex"], function(options) {
options.rules["dependentregex"] = [options.params.regex,
options.params.otherpropertyname,
options.params.otherpropertyregex];
options.messages["dependentregex"] = options.message;
});
Inside my viewmodel I do the following:
[Display(Name = "Customer_PostalCode", ResourceType = typeof(Resources.DisplayNames))]
[DependentRegularExpression("^[1-9]\\d{3} ?[a-zA-Z]{2}$", "CorrespondenceCountry", "Nederland", ErrorMessageResourceType = typeof(Resources.Validation), ErrorMessageResourceName = "Customer_PostalCode")] //"Nederland" is a regular expression in this case
[Required(ErrorMessageResourceType = typeof(Resources.Validation), ErrorMessageResourceName = "Shared_RequiredField")]
public string CorrespondenceZipCode { get; set; }
In the end, the customvalidation.js method basically does the exact same thing as the C# code. A detailed explanation of what everything does can be found in the blogpost I referenced
Upvotes: 2
Reputation: 18749
Take a look at this, I haven't used it myself, but it seems to suit your needs http://foolproof.codeplex.com/workitem/18974
They have an example that looks like this:
[RequiredIf("Country", Operator.RegExMatch, "(1033|4105)", ErrorMessage = "{0} is required")]
public string State { get; set; }
[Required(ErrorMessage = "{0} is required")]
public int Country { get; set; }
Upvotes: 0
Reputation: 21192
It seems like you want a "required if" validation attribute. I would check out http://foolproof.codeplex.com/ - it has an implementation that you can use like so (lifted directly from the project's site):
private class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public bool Married { get; set; }
[RequiredIfTrue("Married")]
public string MaidenName { get; set; }
}
Upvotes: 0