2017-02-03 4 views
3

Я разрабатываю аннотацию пользовательских данных ASP.NET MVC 5.2.3 для проверки в Visual Studio 2015. Для этого необходимо выполнить любое количество полей и убедиться, что, если у вас есть значение, все они должны иметь ценность; если все они пустые/пустые, все должно быть в порядке.ASP.NET MVC выборочная проверка нескольких полей

Несколько примеров помогли:

Однако, я не уверен, как это сделать на стороне клиента проверки, где у вас есть неизвестное количество проверяемых полей.

Как передать это клиенту, используя реализацию метода GetClientValidationRules() интерфейса IClientValidatable?

Также, как применить эту новую аннотацию данных к свойствам моей модели представления? Будет ли это выглядеть так?

[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 Validate расслоения плотной плагин на стороне клиента – Steve

+2

Начать чтение [Полное руководство по валидации в ASP.NET MVC 3 - часть 2] (http://www.devtrends.co.uk/blog/the-complete-guide-to-validation-in-asp.net -mvc-3-часть-2). В методе 'GetClientValidationRules()' вы добавляете 'ModelClientValidationRule', где вы можете передать (например) список разделенных запятыми имен свойств - то есть ваши значения' fields' - которые могут быть проанализированы и использованы в сценариях на стороне клиента (если у вас есть проблемы, дайте мне знать, и я добавлю ответ, но не получаю шанс на несколько часов) –

+0

Спасибо, @StephenMuecke! Одна из моих проблем заключалась в том, как передать значения клиенту. – Alex

ответ

1

Для того, чтобы получить подтверждение на стороне клиента, вам нужно передать значения «других свойств» в ModelClientValidationRule с помощью .Add() метода правил ValidationParameters свойств, а затем записать на стороне клиента сценарии для добавления правил $.validator.

Но сначала есть несколько других проблем, связанных с вашим атрибутом. Сначала вы должны выполнить цикл foreach, только если значение свойства, которое вы применили, это null. Во-вторых, возвращая 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

Его общая функция для поиска связанного элемента в DOM. У вас может быть модель, содержащая свойства, которые являются сложными объектами или коллекциями, поэтому это может быть рендеринг в качестве входов с (скажем) 'name =" Employees [0] .FirstName "id =" Employees_0__FirstName "и' name = " Сотрудники [0] .LastName "id =" Employees_0__LastName ". Так что предположим, что вы хотите проверить' LastName', требуется, если предоставляется 'FirstName'. Все, что вы можете передать в методе GetClientValidationRules(), - это имя другое свойство, т.е. 'Last Name' –

+1

Метод сначала проверяет, содержит ли DOM элемент с идентификатором 'id =" LastName "'. Для простого объекта возвращается элемент. Но в этом случае это не так, поэтому следующая часть функции получает имя текущего элемента (который является 'name =" Employees [0] .FirstName "') и получает часть слева от последней точки (' Employees [0] ') и добавляет к другому свойству, чтобы сгенерировать' Employees [0] .LastName'. Поскольку поиск по 'id' выполняется быстрее, чем по атрибуту' name', '.replace()' генерирует 'Employees_0__LastName' и выполняется поиск этого элемента. –

Смежные вопросы