require_from_group
来自 jquery-validation 团队的使用:
jQuery-validation项目在src文件夹中有一个名为additional的子文件夹。你可以在这里查看。
在该文件夹中,我们有许多不常见的其他验证方法,这就是默认情况下不添加它们的原因。
正如您在该文件夹中看到的那样,它存在很多方法,您需要通过选择您实际需要的验证方法来选择。
根据您的问题,您需要的验证方法是require_from_group
从附加文件夹中命名的。只需下载位于此处的相关文件并将其放入您的Scripts
应用程序文件夹中。
此方法的文档解释了这一点:
让您说“必须填充与选择器 Y 匹配的至少 X 个输入”。
最终结果是这些输入都不是:
...将验证,除非其中至少有一个被填满。
零件号:{require_from_group: [1,".productinfo"]},描述:{require_from_group: [1,".productinfo"]}
options[0]:必须在组中填写的字段数 options 2:定义条件必需字段组的 CSS 选择器
为什么你需要选择这个实现:
此验证方法是通用的,适用于所有input
(文本、复选框、单选等)textarea
和select
. 此方法还允许您指定需要填写的所需输入的最小数量,例如
partnumber: {require_from_group: [2,".productinfo"]},
category: {require_from_group: [2,".productinfo"]},
description: {require_from_group: [2,".productinfo"]}
我创建了两个类RequireFromGroupAttribute
,RequireFromGroupFieldAttribute
这将帮助您进行服务器端和客户端验证
RequireFromGroupAttribute
类定义
RequireFromGroupAttribute
仅源自Attribute
. 该类仅用于配置,例如设置需要填写以进行验证的字段数。您需要向此类提供 CSS 选择器类,验证方法将使用该选择器类来获取同一组中的所有元素。因为必填字段的默认数量为 1,所以此属性仅在指定组中的最低要求大于默认数量时用于装饰您的模型。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class RequireFromGroupAttribute : Attribute
{
public const short DefaultNumber = 1;
public string Selector { get; set; }
public short Number { get; set; }
public RequireFromGroupAttribute(string selector)
{
this.Selector = selector;
this.Number = DefaultNumber;
}
public static short GetNumberOfRequiredFields(Type type, string selector)
{
var requiredFromGroupAttribute = type.GetCustomAttributes<RequireFromGroupAttribute>().SingleOrDefault(a => a.Selector == selector);
return requiredFromGroupAttribute?.Number ?? DefaultNumber;
}
}
RequireFromGroupFieldAttribute
类定义
RequireFromGroupFieldAttribute
它派生自ValidationAttribute
并实现IClientValidatable
. 您需要在模型中参与组验证的每个属性上使用此类。您必须通过 css 选择器类。
[AttributeUsage(AttributeTargets.Property)]
public class RequireFromGroupFieldAttribute : ValidationAttribute, IClientValidatable
{
public string Selector { get; }
public bool IncludeOthersFieldName { get; set; }
public RequireFromGroupFieldAttribute(string selector)
: base("Please fill at least {0} of these fields")
{
this.Selector = selector;
this.IncludeOthersFieldName = true;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var properties = this.GetInvolvedProperties(validationContext.ObjectType); ;
var numberOfRequiredFields = RequireFromGroupAttribute.GetNumberOfRequiredFields(validationContext.ObjectType, this.Selector);
var values = new List<object> { value };
var otherPropertiesValues = properties.Where(p => p.Key.Name != validationContext.MemberName)
.Select(p => p.Key.GetValue(validationContext.ObjectInstance));
values.AddRange(otherPropertiesValues);
if (values.Count(s => !string.IsNullOrWhiteSpace(Convert.ToString(s))) >= numberOfRequiredFields)
{
return ValidationResult.Success;
}
return new ValidationResult(this.GetErrorMessage(numberOfRequiredFields, properties.Values), new List<string> { validationContext.MemberName });
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var properties = this.GetInvolvedProperties(metadata.ContainerType);
var numberOfRequiredFields = RequireFromGroupAttribute.GetNumberOfRequiredFields(metadata.ContainerType, this.Selector);
var rule = new ModelClientValidationRule
{
ValidationType = "requirefromgroup",
ErrorMessage = this.GetErrorMessage(numberOfRequiredFields, properties.Values)
};
rule.ValidationParameters.Add("number", numberOfRequiredFields);
rule.ValidationParameters.Add("selector", this.Selector);
yield return rule;
}
private Dictionary<PropertyInfo, string> GetInvolvedProperties(Type type)
{
return type.GetProperties()
.Where(p => p.IsDefined(typeof(RequireFromGroupFieldAttribute)) &&
p.GetCustomAttribute<RequireFromGroupFieldAttribute>().Selector == this.Selector)
.ToDictionary(p => p, p => p.IsDefined(typeof(DisplayAttribute)) ? p.GetCustomAttribute<DisplayAttribute>().Name : p.Name);
}
private string GetErrorMessage(int numberOfRequiredFields, IEnumerable<string> properties)
{
var errorMessage = string.Format(this.ErrorMessageString, numberOfRequiredFields);
if (this.IncludeOthersFieldName)
{
errorMessage += ": " + string.Join(", ", properties);
}
return errorMessage;
}
}
如何在您的视图模型中使用它?
在您的模型中,这里是如何使用它:
public class SomeViewModel
{
internal const string GroupOne = "Group1";
internal const string GroupTwo = "Group2";
[RequireFromGroupField(GroupOne)]
public bool IsA { get; set; }
[RequireFromGroupField(GroupOne)]
public bool IsB { get; set; }
[RequireFromGroupField(GroupOne)]
public bool IsC { get; set; }
//... other properties
[RequireFromGroupField(GroupTwo)]
public bool IsY { get; set; }
[RequireFromGroupField(GroupTwo)]
public bool IsZ { get; set; }
}
默认情况下,您不需要装饰模型,RequireFromGroupAttribute
因为默认的必填字段数为 1。但如果您希望必填字段的数量与 1 不同,您可以执行以下操作:
[RequireFromGroup(GroupOne, Number = 2)]
public class SomeViewModel
{
//...
}
如何在您的视图代码中使用它?
@model SomeViewModel
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/require_from_group.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
@Html.CheckBoxFor(x => x.IsA, new { @class="Group1"})<span>A</span>
@Html.ValidationMessageFor(x => x.IsA)
<br />
@Html.CheckBoxFor(x => x.IsB, new { @class = "Group1" }) <span>B</span><br />
@Html.CheckBoxFor(x => x.IsC, new { @class = "Group1" }) <span>C</span><br />
@Html.CheckBoxFor(x => x.IsY, new { @class = "Group2" }) <span>Y</span>
@Html.ValidationMessageFor(x => x.IsY)
<br />
@Html.CheckBoxFor(x => x.IsZ, new { @class = "Group2" })<span>Z</span><br />
<input type="submit" value="OK" />
}
请注意,您在使用RequireFromGroupField
属性时指定的组选择器在您的视图中使用,方法是在您的组中涉及的每个输入中将其指定为一个类。
这就是服务器端验证的全部内容。
让我们谈谈客户端验证。
如果您检查类中的GetClientValidationRules
实现,RequireFromGroupFieldAttribute
您会看到我使用的是字符串requirefromgroup
而不是属性require_from_group
的方法名称。ValidationType
这是因为 ASP.Net MVC 只允许验证类型的名称包含字母数字字符,并且不能以数字开头。所以你需要添加以下 javascript :
$.validator.unobtrusive.adapters.add("requirefromgroup", ["number", "selector"], function (options) {
options.rules["require_from_group"] = [options.params.number, options.params.selector];
options.messages["require_from_group"] = options.message;
});
javascript 部分非常简单,因为在适配器函数的实现中,我们只是将验证委托给正确的require_from_group
方法。
因为它适用于每种类型的input
,textarea
和select
元素,我可能认为这种方式更通用。
希望有帮助!