2012-06-04 2 views
3

У меня есть простой вопрос.MVC 3 поле обязательно, если другое поле заполнено

У меня есть, например, две поля, отображаемые на модели ex: textbox_1 и textbox_2.

Я хочу спросить, существует ли способ (ex обязательный декоратор), который налагает textbox_2 обязательно ТОЛЬКО, если я заполняю текстовое поле_1. ЕСЛИ я не заполняю текстовое поле textbox_1, необязательно.

Есть ли элегантный способ сделать это?

+1

Возможное решение с помощью пользовательской проверки атрибутов здесь: http://stackoverflow.com/questions/3713281/attribute-dependent-on-another-field –

ответ

15

В этом случае ASP.NET MVC не существует. Вот атрибут, который я создал для его решения. Есть 3 доступных обыкновения для атрибута:

  • Pass null в targetValue конструктору: требуется только при зависимое поле равно нуль.
  • Передайте любое значение как tagetValue: требуется только в том случае, когда зависимое поле равно значению.
  • Пасс "*" как tagetValue: требуется только при заполнении зависимого поля.

В вашем случае вам необходимо пройти "*"targetValue, как в конструкторе, а это означает, что в зависимости свойство может быть любое не нулевое значение.

Примечание: в нем содержатся как серверная, так и клиентская (+ ненавязчивая) проверка.

сервера класса атрибут сторона:

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable 
{ 
    protected RequiredAttribute _innerAttribute; 

    public string DependentProperty { get; set; } 
    public object TargetValue { get; set; } 

    public bool AllowEmptyStrings 
    { 
     get 
     { 
      return _innerAttribute.AllowEmptyStrings; 
     } 
     set 
     { 
      _innerAttribute.AllowEmptyStrings = value; 
     } 
    } 

    public RequiredIfAttribute(string dependentProperty, object targetValue) 
    { 
     _innerAttribute = new RequiredAttribute(); 
     DependentProperty = dependentProperty; 
     TargetValue = targetValue; 
    } 

    protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
    { 
     // get a reference to the property this validation depends upon 
     var containerType = validationContext.ObjectInstance.GetType(); 
     var field = containerType.GetProperty(DependentProperty); 

     if (field != null) 
     { 
      // get the value of the dependent property 
      var dependentValue = field.GetValue(validationContext.ObjectInstance, null); 
      // trim spaces of dependent value 
      if (dependentValue != null && dependentValue is string) 
      { 
       dependentValue = (dependentValue as string).Trim(); 

       if (!AllowEmptyStrings && (dependentValue as string).Length == 0) 
       { 
        dependentValue = null; 
       } 
      } 

      // compare the value against the target value 
      if ((dependentValue == null && TargetValue == null) || 
       (dependentValue != null && (TargetValue == "*" || dependentValue.Equals(TargetValue)))) 
      { 
       // match => means we should try validating this field 
       if (!_innerAttribute.IsValid(value)) 
        // validation failed - return an error 
        return new ValidationResult(FormatErrorMessage(validationContext.DisplayName), new[] { validationContext.MemberName }); 
      } 
     } 

     return ValidationResult.Success; 
    } 

    public virtual IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context) 
    { 
     var rule = new ModelClientValidationRule 
     { 
      ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()), 
      ValidationType = "requiredif", 
     }; 

     string depProp = BuildDependentPropertyId(metadata, context as ViewContext); 

     // find the value on the control we depend on; 
     // if it's a bool, format it javascript style 
     // (the default is True or False!) 
     string targetValue = (TargetValue ?? "").ToString(); 
     if (TargetValue is bool) 
      targetValue = targetValue.ToLower(); 

     rule.ValidationParameters.Add("dependentproperty", depProp); 
     rule.ValidationParameters.Add("targetvalue", targetValue); 

     yield return rule; 
    } 

    private string BuildDependentPropertyId(ModelMetadata metadata, ViewContext viewContext) 
    { 
     // build the ID of the property 
     string depProp = viewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(DependentProperty); 
     // unfortunately this will have the name of the current field appended to the beginning, 
     // because the TemplateInfo's context has had this fieldname appended to it. Instead, we 
     // want to get the context as though it was one level higher (i.e. outside the current property, 
     // which is the containing object, and hence the same level as the dependent property. 
     var thisField = metadata.PropertyName + "_"; 
     if (depProp.StartsWith(thisField)) 
      // strip it off again 
      depProp = depProp.Substring(thisField.Length); 
     return depProp; 
    } 
} 

на стороне клиента (в том числе ненавязчивой проверки):

$.validator.addMethod('requiredif', 
    function (value, element, parameters) { 
     var id = '#' + parameters['dependentproperty']; 

     // get the target value (as a string, 
     // as that's what actual value will be) 
     var targetvalue = parameters['targetvalue']; 
     targetvalue = (targetvalue == null ? '' : targetvalue).toString(); 

     // get the actual value of the target control 
     // note - this probably needs to cater for more 
     // control types, e.g. radios 
     var control = $(id); 
     var controltype = control.attr('type'); 
     var actualvalue = 
      controltype === 'checkbox' ? 
      control.attr('checked').toString() : 
      control.val(); 

     // if the condition is true, reuse the existing 
     // required field validator functionality 
     if ($.trim(targetvalue) === $.trim(actualvalue) || ($.trim(targetvalue) === '*' && $.trim(actualvalue) !== '')) 
      return $.validator.methods.required.call(
       this, value, element, parameters); 

     return true; 
    }); 

$.validator.unobtrusive.adapters.add(
    'requiredif', 
    ['dependentproperty', 'targetvalue'], 
    function (options) { 
     options.rules['requiredif'] = { 
      dependentproperty: options.params['dependentproperty'], 
      targetvalue: options.params['targetvalue'] 
     }; 
     options.messages['requiredif'] = options.message; 
    }); 
+1

отличное решение, пару вопросов для меня. TargetValue == "*" необходимо изменить на TargetValue.Equals ("*") и – superwalnut

+1

, а также на кнопку inlcue для проверки js. var actualvalue = (controltype === 'checkbox' || controltype === 'radio')? control.attr ('checked'). ToString(): control.val(); – superwalnut

+0

Процедура BuildDependentPropertyId не работает, если свойство находится в классе, который сам по себе является свойством в модели представления более высокого уровня. (В этом случае есть еще один идентификатор в начале строки идентификатора свойства.) Эта статья SO [http://stackoverflow.com/a/21018963/1637105] содержит версию того же типа кода, который работает –

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