2017-02-03 120 views
3

我正在为Visual Studio 2015中的验证开发ASP.NET MVC 5.2.3自定义数据注释。它需要采取任意数量的字段并确保如果有值,他们都必须有价值;如果它们全部为空/空白,则应该没问题。ASP.NET MVC自定义多字段验证

举几个例子帮助:

但是,我不知道该怎么办,你有一个客户端验证未知数量的字段被验证。

您如何通过IClientValidatable接口的GetClientValidationRules()方法的实现将它传递给客户端?

另外,如何将这个新的数据注释应用到我的视图模型的属性?它会是这样吗?

[MultipleRequired("AppNumber", "UserId", /* more fields */), ErrorMessage = "Something..."] 
[DisplayName("App #")] 
public int AppNumber { get; set; } 

[DisplayName("User ID")] 
public int UserId { get; set; } 

这里的,据我可以与MultipleRequiredAttribute自定义数据的注释类获得:

public class MultipleRequiredAttribute : ValidationAttribute, IClientValidatable 
{ 
    private readonly string[] _fields; 
    public MultipleRequiredAttribute(params string[] fields) 
    { 
     _fields = fields; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     // If any field has value, then all must have value 
     var anyHasValue = _fields.Any(f => !string.IsNullOrEmpty(f)); 

     if (!anyHasValue) return null; 

     foreach (var field in _fields) 
     { 
      var property = validationContext.ObjectType.GetProperty(field); 
      if (property == null) 
       return new ValidationResult($"Property '{field}' is undefined."); 

      var fieldValue = property.GetValue(validationContext.ObjectInstance, null); 

      if (string.IsNullOrEmpty(fieldValue?.ToString())) 
       return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
     } 

     return null; 
    } 

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     yield return new ModelClientValidationRule 
     { 
      ErrorMessage = ErrorMessage, 
      ValidationType = "multiplerequired" 
     }; 
    } 
} 

谢谢。

+0

你建立jQuery验证JS在客户端插件自定义函数 – Steve

+2

首先阅读[ASP.NET MVC 3验证完整指南 - 第二部分](http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net -mvc -3-部分-2)。在你的'GetClientValidationRules()'方法中,你添加一个'ModelClientValidationRule',你可以在其中传递一个逗号分隔的属性名称列表 - 也就是你的'fields'值 - 可以在客户端脚本中解析和使用如果你有问题,请告诉我,我会添加一个答案,但不会有机会几个小时) –

+0

谢谢@StephenMuecke!我的问题之一是如何将价值传递给客户。 – Alex

回答

1

为了让客户端验证,则需要通过使用规则的.Add()方法ValidationParameters属性传中ModelClientValidationRule的“其他属性”的值,然后写客户端脚本的规则添加到$.validator

但首先还有一些其他问题需要解决您的属性。首先,只有在您应用属性的属性的值为null时,才应该执行foreach循环。其次,如果其中一个'其他属性'不存在,则返回ValidationResult对用户来说是混淆和无意义的,您应该忽略它。

属性代码应为(注意,我改变了属性的名称)

public class RequiredIfAnyAttribute : ValidationAttribute, IClientValidatable 
{ 
    private readonly string[] _otherProperties; 
    private const string _DefaultErrorMessage = "The {0} field is required"; 

    public RequiredIfAnyAttribute(params string[] otherProperties) 
    { 
     if (otherProperties.Length == 0) // would not make sense 
     { 
      throw new ArgumentException("At least one other property name must be provided"); 
     } 
     _otherProperties = otherProperties; 
     ErrorMessage = _DefaultErrorMessage; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     if (value == null) // no point checking if it has a value 
     { 
      foreach (string property in _otherProperties) 
      { 
       var propertyName = validationContext.ObjectType.GetProperty(property); 
       if (propertyName == null) 
       { 
        continue; 
       } 
       var propertyValue = propertyName.GetValue(validationContext.ObjectInstance, null); 
       if (propertyValue != null) 
       { 
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
       } 
      } 
     } 
     return ValidationResult.Success; 
    } 
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     var rule = new ModelClientValidationRule 
     { 
      ValidationType = "requiredifany", 
      ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()), 
     }; 
     /pass a comma separated list of the other propeties 
     rule.ValidationParameters.Add("otherproperties", string.Join(",", _otherProperties)); 
     yield return rule; 
    } 
} 

脚本届时将

sandtrapValidation = { 
    getDependentElement: function (validationElement, dependentProperty) { 
     var dependentElement = $('#' + dependentProperty); 
     if (dependentElement.length === 1) { 
      return dependentElement; 
     } 
     var name = validationElement.name; 
     var index = name.lastIndexOf(".") + 1; 
     var id = (name.substr(0, index) + dependentProperty).replace(/[\.\[\]]/g, "_"); 
     dependentElement = $('#' + id); 
     if (dependentElement.length === 1) { 
      return dependentElement; 
     } 
     // Try using the name attribute 
     name = (name.substr(0, index) + dependentProperty); 
     dependentElement = $('[name="' + name + '"]'); 
     if (dependentElement.length > 0) { 
      return dependentElement.first(); 
     } 
     return null; 
    } 
} 

$.validator.unobtrusive.adapters.add("requiredifany", ["otherproperties"], function (options) { 
    var element = options.element; 
    var otherNames = options.params.otherproperties.split(','); 
    var otherProperties = []; 
    $.each(otherNames, function (index, item) { 
     otherProperties.push(sandtrapValidation.getDependentElement(element, item)) 
    }); 
    options.rules['requiredifany'] = { 
     otherproperties: otherProperties 
    }; 
    options.messages['requiredifany'] = options.message; 
}); 

$.validator.addMethod("requiredifany", function (value, element, params) { 
    if ($(element).val() != '') { 
     // The element has a value so its OK 
     return true; 
    } 
    var isValid = true; 
    $.each(params.otherproperties, function (index, item) { 
     if ($(this).val() != '') { 
      isValid = false; 
     } 
    }); 
    return isValid; 
}); 
+0

谢谢,@StephenMuecke。我迷失了'sandtrapValidation'代码的逻辑;你介意解释一下吗? – Alex

+1

它是一个通用函数,用于查找关联元素(比如说)'name =“Employees [0] .FirstName”id =“Employees_0__FirstName”''和'name =“的输入可以渲染为输入。 Employees [0] .LastName“id =”Employees_0__LastName“'。假设你想要验证'LastName'是否需要,如果提供了'FirstName',你可以通过'GetClientValidationRules()'方法传入其他财产,即'最后名称' –

+1

该方法首先检查DOM是否包含具有'id =“LastName”'的元素。对于一个简单的对象,这将返回一个元素。但在这种情况下,它不会,所以该函数的下一部分获取当前元素的名称(它是'name =“Employees [0] .FirstName”'),并获取最后一个点左边的部分(' Employees [0]')并附加到其他属性以生成Employees [0] .LastName。由于'id'的搜索速度快于'name'属性,因此'.replace()'生成'Employees_0__LastName'并且执行该元素的搜索。 –