Necesito validar 3 o más campos de entrada (obligatorio al menos uno). Por ejemplo tengo correo electrónico, fax, teléfono.
Requiero que se complete al menos UNO. Necesito tanto el servidor como el cliente ‘validación discreta’. por favor ayuda. Busqué en el método «Comparar» e intenté modificarlo, pero no tuve suerte. por favor ayuda. gracias
Solución
Puede escribir un atributo personalizado:
public class AtLeastOneRequiredAttribute : ValidationAttribute, IClientValidatable
{
private readonly string[] _properties;
public AtLeastOneRequiredAttribute(params string[] properties)
{
_properties = properties;
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if (_properties == null || _properties.Length < 1)
{
return null;
}
foreach (var property in _properties)
{
var propertyInfo = validationContext.ObjectType.GetProperty(property);
if (propertyInfo == null)
{
return new ValidationResult(string.Format("unknown property {0}", property));
}
var propertyValue = propertyInfo.GetValue(validationContext.ObjectInstance, null);
if (propertyValue is string && !string.IsNullOrEmpty(propertyValue as string))
{
return null;
}
if (propertyValue != null)
{
return null;
}
}
return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = ErrorMessage,
ValidationType = "atleastonerequired"
};
rule.ValidationParameters["properties"] = string.Join(",", _properties);
yield return rule;
}
}
que podría usarse para decorar una de las propiedades del modelo de vista (la que desea resaltar si falla la validación):
public class MyViewModel
{
[AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")]
public string Email { get; set; }
public string Fax { get; set; }
public string Phone { get; set; }
}
y luego un controlador simple:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
Representación de la siguiente vista que se encargará de definir el adaptador de validador del lado del cliente personalizado:
@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(
'atleastonerequired', ['properties'], function (options) {
options.rules['atleastonerequired'] = options.params;
options.messages['atleastonerequired'] = options.message;
}
);
jQuery.validator.addMethod('atleastonerequired', function (value, element, params) {
var properties = params.properties.split(',');
var values = $.map(properties, function (property, index) {
var val = $('#' + property).val();
return val != '' ? val : null;
});
return values.length > 0;
}, '');
</script>
@using (Html.BeginForm())
{
@Html.ValidationSummary(false)
<div>
@Html.LabelFor(x => x.Email)
@Html.EditorFor(x => x.Email)
</div>
<div>
@Html.LabelFor(x => x.Fax)
@Html.EditorFor(x => x.Fax)
</div>
<div>
@Html.LabelFor(x => x.Phone)
@Html.EditorFor(x => x.Phone)
</div>
<input type="submit" value="OK" />
}
Por supuesto, el adaptador personalizado y la regla del validador deben externalizarse en un archivo javascript separado para evitar mezclar el script con el marcado.
Otras respuestas
Ya que está utilizando MVC 3, eche un vistazo al excelente video que Brad Wilson tuvo en mvcConf. Hay todo lo que necesita para crear una validación discreta de cliente + servidor