viernes, 11 de mayo de 2012

ASP.NET MVC4 Validando un campo que depende de otro

Parece que estoy destinado a tropezarme con algunos problemas cada vez que quiero validar algo en las clases modelo de mis vistas. Lla parte positiva es que uno sigue aprendiendo, así que, vamos al lio. Como siempre, supongamos esta vista y este modelo simplificados

Vista
<h2>Index</h2>
@using (Html.BeginForm())
{
 <div>Otros @Html.CheckBoxFor(m => m.Otros)</div>
 <div>Comentario @Html.TextBoxFor(m => m.Comentario)</div>
 <div>@Html.ValidationMessageFor(m => m.Comentario)</div>
 <input type="submit" value="Enviar" />
}
Modelo
public class IndexModel
{
 public bool Otros { get; set; }
 public string Comentario { get; set; }
}
Como siempre, nada raro. Se trata de validar el campo Comentario siempre y cuando el valor de campo Otros sea verdadero. Una vez más busqué algo que de serie me hiciera esta validación pero no encontré nada, así que una vez más me tocó hacer mi propio validador. Para crear un validador simplemente tenemos que heredar de la clase ValidationAttribute y sobreescribir el método IsValid. Pero tendremos que sobreescribir el método IsValid que tiene dos argumentos (object value y ValidationContext validationContext) ya que tenemos que acceder al contexto de validación para obtener el valor de una propiedad. El esqueleto básico del validador sería algo así
public class RequiredIfTrue : ValidationAttribute
{
  protected override ValidationResult IsValid(object value, ValidationContext validationContext)
  {
    return base.IsValid(value, validationContext);
  }
}
Bien, lo primero que haremos será definir una propiedad (TrueOrFalsePropertyName) en nuestro validador para indicar el nombre de la propiedad enlazada. En nuestro caso, es la propiedad que deberá tener el valor "verdadero" para seguir con la validación, o sea, la propiedad Otros. Luego, en el método IsValid, accederemos a esa propiedad, obtendremos su valor, y comprobaremos si tenemos o no que continuar con la validación. También haremos algunas validaciones como que el nombre de la propiedad es válido y que el tipo que devuelve es un boolean En estos casos, he optado por lanzar una excepción para detener el flujo del programa ya que considero que son fallos de "programación" y que estos fallos no llegarían al usuario final. Con estás premisas nuestro validador queda de la siguiente forma
public class RequiredIfTrue : ValidationAttribute
{
 public string TrueOrFalsePropertyName { get; set; }

 protected override ValidationResult IsValid(object value, ValidationContext validationContext)
 {
  if (string.IsNullOrEmpty(TrueOrFalsePropertyName))
   throw new Exception("No se ha especificado la propiedad 'TrueOrFalsePropertyName'");

  // Obtenemos la propiedad
  PropertyInfo property = validationContext.ObjectType.GetProperty(TrueOrFalsePropertyName);
  if (property == null) 
   throw new Exception(string.Format("No se encuentra la propiedad '{0}'", TrueOrFalsePropertyName));
  
  // Comprobamos que la propiedad sea de tipo System.Boolean
  if (property.PropertyType != typeof(System.Boolean))
   throw new Exception(string.Format("La propiedad '{0}' no es de tipo System.Boolean", TrueOrFalsePropertyName));

  bool pValue = (bool)property.GetValue(validationContext.ObjectInstance, null);

  // Comprobamos el valor
  if (pValue && (value == null || (string)value == string.Empty)) return new ValidationResult(ErrorMessage);
  
  return null;
 }
}
Y el funcionamiento del validador se puede ver en las siguientes imágenes.



Si no marcamos el campo otros da igual el valor


Si marcamos el campo Otros, el campo Comentario es obligatorio.


Todo correcto otra vez

Happy coding!

Nota: Y para estrenar mi cuenta en SkyDrive, se pueden descargar el código fuente de los siguientes enlaces.
MvcApplication1.rar
MvcApplication1.zip

No hay comentarios:

Publicar un comentario