2017-02-03 132 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'並且執行該元素的搜索。 –