Asp.net mvc MVC3验证-需要来自组的验证
给定以下视图模型:Asp.net mvc MVC3验证-需要来自组的验证,asp.net-mvc,asp.net-mvc-3,jquery-validate,unobtrusive-validation,Asp.net Mvc,Asp.net Mvc 3,Jquery Validate,Unobtrusive Validation,给定以下视图模型: public class SomeViewModel { public bool IsA { get; set; } public bool IsB { get; set; } public bool IsC { get; set; } //... other properties } 我希望创建一个自定义属性来验证至少一个可用属性是否为true。我设想能够将属性附加到属性并指定组名,如下所示: public class SomeViewModel {
public class SomeViewModel
{
public bool IsA { get; set; }
public bool IsB { get; set; }
public bool IsC { get; set; }
//... other properties
}
我希望创建一个自定义属性来验证至少一个可用属性是否为true。我设想能够将属性附加到属性并指定组名,如下所示:
public class SomeViewModel
{
[RequireAtLeastOneOfGroup("Group1")]
public bool IsA { get; set; }
[RequireAtLeastOneOfGroup("Group1")]
public bool IsB { get; set; }
[RequireAtLeastOneOfGroup("Group1")]
public bool IsC { get; set; }
//... other properties
[RequireAtLeastOneOfGroup("Group2")]
public bool IsY { get; set; }
[RequireAtLeastOneOfGroup("Group2")]
public bool IsZ { get; set; }
}
我希望在提交表单之前在客户端验证表单中的值是否发生变化,这就是为什么我希望尽可能避免使用类级属性的原因
这将需要服务器端和客户端验证来定位具有相同组名值(作为自定义属性的参数传入)的所有属性。这可能吗?非常感谢您的指导。这里有一种方法可以继续(还有其他方法,我只是演示一种与您的视图模型相匹配的方法):
还有一个观点:
@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>
@using (Html.BeginForm())
{
@Html.EditorFor(x => x.IsA)
@Html.ValidationMessageFor(x => x.IsA)
<br/>
@Html.EditorFor(x => x.IsB)<br/>
@Html.EditorFor(x => x.IsC)<br/>
@Html.EditorFor(x => x.IsY)
@Html.ValidationMessageFor(x => x.IsY)
<br/>
@Html.EditorFor(x => x.IsZ)<br/>
<input type="submit" value="OK" />
}
@model SomeViewModel
@使用(Html.BeginForm())
{
@EditorFor(x=>x.IsA)
@Html.ValidationMessageFor(x=>x.IsA)
@Html.EditorFor(x=>x.IsB)
@Html.EditorFor(x=>x.IsC)
@EditorFor(x=>x.IsY)
@Html.ValidationMessageFor(x=>x.IsY)
@Html.EditorFor(x=>x.IsZ)
}
剩下的最后一部分是为客户端验证注册适配器:
jQuery.validator.unobtrusive.adapters.add(
'group',
[ 'propertynames' ],
function (options) {
options.rules['group'] = options.params;
options.messages['group'] = options.message;
}
);
jQuery.validator.addMethod('group', function (value, element, params) {
var properties = params.propertynames.split(',');
var isValid = false;
for (var i = 0; i < properties.length; i++) {
var property = properties[i];
if ($('#' + property).is(':checked')) {
isValid = true;
break;
}
}
return isValid;
}, '');
jQuery.validator.unobtrusive.adapters.add(
"集团",,
[“propertynames”],
功能(选项){
options.rules['group']=options.params;
options.messages['group']=options.message;
}
);
jQuery.validator.addMethod('group',函数(值、元素、参数){
var properties=params.propertynames.split(',');
var isValid=false;
对于(var i=0;i
根据您的具体要求,可能会对代码进行修改。我知道这是一个老问题,但我刚刚遇到了相同的情况,找到了一些解决方案,并看到了一个解决Matt上述问题的方案,因此我想我会与遇到此答案的人分享。检查:我在应用程序中实现了Darin的绝妙答案,除了我为字符串而不是布尔值添加了它。这是针对姓名/公司或电话/电子邮件之类的内容。我喜欢它,除了一点小小的吹毛求疵 我试图在没有工作电话、手机、家庭电话或电子邮件的情况下提交表格。我在客户端发现了四个独立的验证错误。这对我来说很好,因为它让用户确切地知道可以填写哪些字段来消除错误 我输入了一个电子邮件地址。现在,电子邮件下的单一验证消失了,但电话号码下的三个验证仍然存在。这些也不再是错误了 因此,我重新分配了检查验证的jQuery方法来解释这一点。代码如下。希望它能帮助别人
jQuery.validator.prototype.check = function (element) {
var elements = [];
elements.push(element);
var names;
while (elements.length > 0) {
element = elements.pop();
element = this.validationTargetFor(this.clean(element));
var rules = $(element).rules();
if ((rules.group) && (rules.group.propertynames) && (!names)) {
names = rules.group.propertynames.split(",");
names.splice($.inArray(element.name, names), 1);
var name;
while (name = names.pop()) {
elements.push($("#" + name));
}
}
var dependencyMismatch = false;
var val = this.elementValue(element);
var result;
for (var method in rules) {
var rule = { method: method, parameters: rules[method] };
try {
result = $.validator.methods[method].call(this, val, element, rule.parameters);
// if a method indicates that the field is optional and therefore valid,
// don't mark it as valid when there are no other rules
if (result === "dependency-mismatch") {
dependencyMismatch = true;
continue;
}
dependencyMismatch = false;
if (result === "pending") {
this.toHide = this.toHide.not(this.errorsFor(element));
return;
}
if (!result) {
this.formatAndAdd(element, rule);
return false;
}
} catch (e) {
if (this.settings.debug && window.console) {
console.log("Exception occurred when checking element " + element.id + ", check the '" + rule.method + "' method.", e);
}
throw e;
}
}
if (dependencyMismatch) {
return;
}
if (this.objectLength(rules)) {
this.successList.push(element);
}
}
return true;
};
使用jquery验证团队的require\u组:
jqueryvalidation项目在src文件夹中有一个子文件夹,名为additional。
你可以查一下
在这个文件夹中,我们有很多其他的验证方法,这些方法并不常见,这就是为什么默认情况下不添加它们的原因
正如您在该文件夹中看到的,它存在着许多方法,您需要通过选择实际需要的验证方法来进行选择
根据您的问题,您需要的验证方法名为“附加文件夹中的”require\u from\u group
。
只需下载位于的此关联文件,并将其放入您的Scripts
应用程序文件夹中
此方法的文档说明了这一点:
允许您说“必须填充至少X个与选择器Y匹配的输入。”
最终结果是,这两种输入均不:
…将验证,除非其中至少有一个已填充
零件号:{require_from_组:[1,“.productinfo”]},
描述:{require_from_组:[1,“.productinfo”]}
选项[0]:组中必须填写的字段数
选项:CSS选择器,用于定义条件必需的字段组
为什么需要选择此实现:
此验证方法是通用的,适用于每个输入
(文本、复选框、收音机等)、文本区域
和选择
此方法还允许您指定需要填写的所需输入的最小数量,例如
partnumber: {require_from_group: [2,".productinfo"]},
category: {require_from_group: [2,".productinfo"]},
description: {require_from_group: [2,".productinfo"]}
我创建了两个类RequireFromGroupAttribute
和RequireFromGroupFieldAttribute
,它们将帮助您进行服务器端和客户端验证
RequireFromGroupAttribute
类定义
RequireFromGroupAttribute
仅派生自属性
。该类仅用于配置,例如设置验证需要填写的字段数。您需要为此类提供CSS选择器类,验证方法将使用该类获取同一组中的所有元素。由于必填字段的默认数目为1,因此只有当SPCIED组中的最低要求大于默认数目时,此属性才用于修饰模型
[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;
}
}
如何在视图模型中使用它?
在您的模型中,以下是如何使用它:
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
{
//...
}
如何在视图代码中使用它?
javascript部分非常简单,因为在Adapter函数的实现中,我们只是将验证委托给正确的require\u from\u group
方法
因为它适用于每种类型的输入
,文本区域
和选择
元素,所以
[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; }
}
[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" />
}
$.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;
});