首先,您需要以与比较属性类似的方式创建自己的验证属性。
在此属性中,您将指定其他依赖属性,错误消息的格式将考虑属性显示名称。
该属性看起来像这样(我对它的名称和默认错误消息不太自豪!):
public class SelectOneValidationAttribute : ValidationAttribute, IClientValidatable
{
private const string DefaultErrorMessage = "Please enter '{0}' or '{1}'.";
private string _otherFieldName;
public SelectOneValidationAttribute(String otherFieldName)
: base(DefaultErrorMessage)
{
_otherFieldName = otherFieldName;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentCulture, ErrorMessageString, name, _otherFieldName);
}
protected override DataAnnotationsValidationResult IsValid(object value, ValidationContext validationContext)
{
PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(_otherFieldName);
if (otherPropertyInfo == null)
{
return new DataAnnotationsValidationResult("Unknown property " + _otherFieldName);
}
string strValue = value == null ? null : value.ToString();
if(!String.IsNullOrEmpty(strValue))
//validation succeeds as this field has been populated
return null;
object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
string strOtherPropertyValue = otherPropertyValue == null ? null : otherPropertyValue.ToString();
if (!String.IsNullOrEmpty(strOtherPropertyValue))
//validation succeeds as the other field has been populated
return null;
else
//validation failed, none of the 2 fields were populated
return new DataAnnotationsValidationResult(DefaultErrorMessage);
}
//Create the data attributes for the client to use
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ValidationType = "selectone",
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName())
};
rule.ValidationParameters["otherfield"] = FormatPropertyForClientValidation(_otherFieldName);
yield return rule;
}
public static string FormatPropertyForClientValidation(string property)
{
return "*." + property;
}
}
因此,您可以在模型中使用它,如下所示:
public class YourModel
{
[SelectOneValidation("Phone")]
public string Mobile { get; set; }
[SelectOneValidation("Mobile")]
public string Phone { get; set; }
}
使用该代码,错误消息“请输入‘手机’或‘电话’。” 和“请输入‘电话’或‘手机’。” 将在服务器端验证失败时显示。(您可以在两者上设置相同的错误消息,例如“请输入...”)
为了添加客户端验证,您需要为不显眼的验证创建适配器。(确保在不显眼的验证解析文档之前将其添加到某处,否则您将需要手动解析它):
//Add an adaptor for our new jquery validator, that builds the optional parameters
//for our validation code (the other field to look at)
$.validator.unobtrusive.adapters.add("selectone", ["otherfield"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.otherfield,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
setValidationValues(options, "selectone", element);
});
这是使用在不显眼的验证库中定义的一些实用程序函数,例如:
function setValidationValues(options, ruleName, value) {
options.rules[ruleName] = value;
if (options.message) {
options.messages[ruleName] = options.message;
}
}
function getModelPrefix(fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf(".") + 1);
}
function appendModelPrefix(value, prefix) {
if (value.indexOf("*.") === 0) {
value = value.replace("*.", prefix);
}
return value;
}
最后我创建了一个新的验证规则。这是因为尽管所需的验证允许设置过滤表达式,因此该字段仅在该表达式被评估为 true 时才被标记为有效,请参阅required validation,我们需要对 onBlur 验证应用与 equalto 规则类似的修复。
验证方法是从 equalto 验证中应用上述修复,然后仅在其他相关字段上使用依赖选择器调用所需规则,因此仅当字段为空且依赖项尚未填充时,所需验证才返回 false。
$.validator.addMethod("selectone", function (value, element, param) {
// bind to the blur event of the target in order to revalidate whenever the target field is updated
// TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
var otherField = $(param);
if (this.settings.onfocusout) {
otherField.unbind(".validate-selectone").bind("blur.validate-selectone", function () {
$(element).valid();
});
}
var otherFieldBlankFilter = ":input[name=" + otherField[0].name + "]:blank";
return $.validator.methods.required.call(this, value, element, otherFieldBlankFilter);
});
如果您不介意这一点,就好像您仅在提交表单时进行验证,您可以编写一个直接使用所需规则的适配器:
$.validator.unobtrusive.adapters.add("selectone", ["otherfield"], function (options) {
var prefix = getModelPrefix(options.element.name),
other = options.params.otherfield,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(":input[name=" + fullOtherName + "]")[0];
setValidationValues(options, "required", ":input[name=" + fullOtherName + "]:blank");
});
有了所有这些,您的验证应该可以工作。如果两者都是空的并且您尝试提交,则会为这两个字段显示错误消息。如果您随后在其中一个中输入内容并标记出来,则应删除两条错误消息。
您还应该能够根据自己的需要进行调整,因为您可以修改验证属性、不显眼的适配器和验证规则。