Una posibilidad es escribir un atributo de validación personalizada:
public class RequiredIfOtherFieldIsNullAttribute : ValidationAttribute, IClientValidatable
{
private readonly string _otherProperty;
public RequiredIfOtherFieldIsNullAttribute(string otherProperty)
{
_otherProperty = otherProperty;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_otherProperty);
if (property == null)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
"Unknown property {0}",
new[] { _otherProperty }
));
}
var otherPropertyValue = property.GetValue(validationContext.ObjectInstance, null);
if (otherPropertyValue == null || otherPropertyValue as string == string.Empty)
{
if (value == null || value as string == string.Empty)
{
return new ValidationResult(string.Format(
CultureInfo.CurrentCulture,
FormatErrorMessage(validationContext.DisplayName),
new[] { _otherProperty }
));
}
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
ValidationType = "requiredif",
};
rule.ValidationParameters.Add("other", _otherProperty);
yield return rule;
}
}
la que se aplicaría a una de las propiedades de su modelo de vista:
public class MyViewModel
{
[RequiredIfOtherFieldIsNull("Mobile")]
public string Phone { get; set; }
public string Mobile { get; set; }
}
entonces usted podría tener un controlador:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
y, finalmente, una vista en los que se registrará un adaptador para conectar la validación del lado del cliente para esta regla personalizada:
@model MyViewModel
<script src="@Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
jQuery.validator.unobtrusive.adapters.add(
'requiredif', ['other'], function (options) {
var getModelPrefix = function (fieldName) {
return fieldName.substr(0, fieldName.lastIndexOf('.') + 1);
}
var appendModelPrefix = function (value, prefix) {
if (value.indexOf('*.') === 0) {
value = value.replace('*.', prefix);
}
return value;
}
var prefix = getModelPrefix(options.element.name),
other = options.params.other,
fullOtherName = appendModelPrefix(other, prefix),
element = $(options.form).find(':input[name="' + fullOtherName + '"]')[0];
options.rules['requiredif'] = element;
if (options.message) {
options.messages['requiredif'] = options.message;
}
}
);
jQuery.validator.addMethod('requiredif', function (value, element, params) {
var otherValue = $(params).val();
if (otherValue != null && otherValue != '') {
return true;
}
return value != null && value != '';
}, '');
</script>
@using (Html.BeginForm())
{
<div>
@Html.LabelFor(x => x.Phone)
@Html.EditorFor(x => x.Phone)
@Html.ValidationMessageFor(x => x.Phone)
</div>
<div>
@Html.LabelFor(x => x.Mobile)
@Html.EditorFor(x => x.Mobile)
@Html.ValidationMessageFor(x => x.Mobile)
</div>
<button type="submit">OK</button>
}
Cosas bastante enfermo por algo tan extremadamente fácil como regla de validación que encontramos en nuestra vida cotidiana. No sé qué han pensado los diseñadores de ASP.NET MVC cuando decidieron elegir un enfoque declarativo para la validación en lugar de un imperativo.
De todos modos, es por eso que utilizo FluentValidation.NET en lugar de anotaciones de datos para realizar validaciones en mis modelos. La implementación de tales escenarios simples de validación se implementa de una manera que debería ser: simple.
Después de su recomendación, configuré FluentValidation. El problema que estaba tratando de resolver es el mismo que el de esta pregunta, y el validador When() en FV lo resuelve. El problema que tengo es que no hay una validación del lado del cliente para "When()". No soy demasiado cluey en jquery validation atm, pero ¿hay mucho involucrado en la creación de una validación del cliente para ello? Supongo que sería algo similar a lo que hay en tu respuesta, ¿eh? – jzm
Podría heredar de 'RequiredAttribute' para aprovechar el mensaje de error incorporado. –