Вот что у меня получилось: Серверная часть:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Web.Mvc;
namespace Atlas.Core.Attributes
{
/// <summary>
/// Add the following decoration: [ConditionalRequired("Model", "Field")]
/// Model = client model being used to bind object
/// Field = the field that if not null makes this field required.
/// </summary>
public class ConditionalRequiredAttribute : ValidationAttribute, IClientValidatable
{
private const string DefaultErrorMessageFormatString = "The {0} field is required.";
private readonly string _dependentPropertyPrefix;
private readonly string _dependentPropertyName;
public ConditionalRequiredAttribute(string dependentPropertyPrefix, string dependentPropertyName)
{
_dependentPropertyPrefix = dependentPropertyPrefix;
_dependentPropertyName = dependentPropertyName;
ErrorMessage = DefaultErrorMessageFormatString;
}
protected override ValidationResult IsValid(object item, ValidationContext validationContext)
{
PropertyInfo property = validationContext.ObjectInstance.GetType().GetProperty(_dependentPropertyName);
object dependentPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (dependentPropertyValue != null && item == null)
return new ValidationResult(string.Format(ErrorMessageString, validationContext.DisplayName));
return ValidationResult.Success;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = string.Format("{0} is required", metadata.GetDisplayName()),
ValidationType = "conditionalrequired",
};
rule.ValidationParameters.Add("requiredpropertyprefix", _dependentPropertyPrefix);
rule.ValidationParameters.Add("requiredproperty", _dependentPropertyName);
yield return rule;
}
}
}
Сторона клиента:
$.validator.unobtrusive.adapters.add('conditionalrequired', ['requiredpropertyprefix', 'requiredproperty'], function (options) {
options.rules['conditionalrequired'] = {
requiredpropertyprefix: options.params['requiredpropertyprefix'],
requiredproperty: options.params['requiredproperty']
};
options.messages['conditionalrequired'] = options.message;
});
$.validator.addMethod('conditionalrequired', function (value, element, params) {
var requiredpropertyprefix = params['requiredpropertyprefix'];
var requiredproperty = params['requiredproperty'];
var field = $('#' + requiredproperty).length == 0 ? '#' + requiredpropertyprefix + '_' + requiredproperty : '#' + requiredproperty;
return !($(field).val().length > 0 && value.length == 0);
}
);
Я настроил это, чтобы принять модель или значение префикса, а затем имя фактического поля. Причина этого в том, что во многих случаях я буду добавлять объект как часть модели, и это приведет к тому, что идентификатор формы для этого элемента будет отображаться как ModelName_FieldName. Но мне также пришло в голову, что вы можете или не можете использовать модель со встроенным объектом. В этом случае идентификатор будет просто FieldName, поэтому код на стороне клиента проверяет, существует ли элемент по FieldName, и если нет, он возвращает ModelName_FieldName, в противном случае он просто возвращает FieldName. Я еще этого не делал, но, вероятно, мне следует проверить, чтобы полученное имя поля не было нулевым.
а затем, чтобы украсить свою собственность, вы должны сделать что-то вроде этого:
[DataMember]
[DisplayName("Approved By")]
[ConditionalRequired("HOA", "ApprovedDate")]
[StringLength(50, ErrorMessage = "{0} must not exceed {1} characters")]
public string ApprovedBy { get; set; }
моя модель выглядит так:
public class HOAModel
{
public HOA HOA { get; set; }
}
моя реализация представления выглядит так:
Html.Kendo().DatePickerFor(m => m.HOA.ApprovedDate)
Итак, мой элемент на стороне клиента имеет следующий идентификатор:
<input name="HOA.ApprovedDate" class="k-input" id="HOA_ApprovedDate" role="textbox">
person
Jim Shaffer
schedule
13.11.2014