MVC3 grupo de validación discreto de entradas

Necesito validar 3 o más campos de entrada (requerido al menos uno). Por ejemplo, tengo correo electrónico, fax, teléfono.

Necesito que se complete al menos UNA. Necesito una “validación no intrusiva” del servidor y del cliente. por favor ayuda. Busqué en el método “Comparar” e intenté modificarlo pero no tuve suerte. por favor ayuda. Gracias

Podrías 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 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 de su modelo de vista (la que desea resaltar si la validación falla):

 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); } } 

Presentar la siguiente vista que se ocupará de definir el adaptador de validación personalizado del lado del cliente:

 @model MyViewModel    @using (Html.BeginForm()) { @Html.ValidationSummary(false) 
@Html.LabelFor(x => x.Email) @Html.EditorFor(x => x.Email)
@Html.LabelFor(x => x.Fax) @Html.EditorFor(x => x.Fax)
@Html.LabelFor(x => x.Phone) @Html.EditorFor(x => x.Phone)
}

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.

Pasé más de 36 horas por qué el código no funcionó para mí … Al final, descubrí que en mi caso, se suponía que no debía usar los nombres de las propiedades en esta línea de código.

 [AtLeastOneRequired("Email", "Fax", "Phone", ErrorMessage = "At least Email, Fax or Phone is required")] 

Pero tuve que usar el HTMl elemento Ids en lugar de los nombres de propiedad y funcionó como magia.

Publicando esto aquí si podría ayudar a alguien.

Ya que está utilizando MVC 3, eche un vistazo al excelente video que Brad Wilson hizo en mvcConf. Hay todo lo que necesita para crear validación discreta cliente + servidor

La solución de @Darin Kimitrov es probablemente el estándar para crear un atributo de validación personalizado que funcione con una validación discreta. Sin embargo, el uso de atributos de validación personalizados para la validación discreta tiene algunas desventajas, tales como:

  • El atributo de validación personalizado solo se adjunta a una propiedad, por lo que la validación del cliente no funcionará si hay un evento de cambio en las otras dos entradas.
  • El mensaje de error funciona bien con ValidationSummary, pero si desea mostrar 1 mensaje de error para todo el grupo (que creo que es la norma), sería casi imposible.
  • Para evitar el primer problema, podemos agregar un atributo de validación personalizado a cada elemento del grupo, lo que causará otro problema: debemos validar todos los elementos del grupo, en lugar de detenernos en el primer elemento de grupo no válido. Y, por supuesto, el segundo problema – mensajes de error separados para cada elemento – aún permanece.

Hay otra manera de manejar la validación del grupo de entradas a través del cliente, usando la configuración de grupos en el validador de jquery ( https://jqueryvalidation.org/validate/#groups ). El único problema (y uno grande) es que la validación discreta no admite los grupos de validación de jquery de forma predeterminada, por lo que debemos personalizar un poco.

Aunque esta respuesta no es “discreta”, en mi opinión, vale la pena tratar de deshacerse de una complicación innecesaria del código, si su objective final es validar un grupo de entradas mientras utiliza la discreta biblioteca de validación de Microsoft.

Primero, debido a que la configuración de grupos del validador de jquery predeterminado no está disponible en el validador de jquery no intrusivo, tenemos que anular las configuraciones discretas (ref. ¿Cómo puedo personalizar la validación discreta en ASP.NET MVC 3 para que coincida con mi estilo? )

 $("form").on('submit', function () { var form = this; var validator = $(this).data("validator"); if (validator.settings && !validator.settings.submitHandler) { $.extend(true, validator.settings.rules, validationSettings.rules); $.extend(true, validator.settings.groups, validationSettings.groups); initGroups(validator); var fnErrorReplacement = validator.settings.errorPlacement; validator.settings.errorPlacement = function (error, element) { validationSettings.errorPlacement(error, element, fnErrorReplacement, form); } validator.settings.submitHandler = formSubmitHandler; } }); function formSubmitHandler(form) { form.submit(); } 

Después de eso, anule los discretos grupos de validadores, las reglas y la configuración del error de ubicación.

 var validationSettings = { groups: { checkboxgroup: "Email Fax Phone" }, rules: { Email: { required: function () { return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]); } }, Fax: { required: function () { return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]); } }, Phone: { required: function () { return validateCheckboxGroup(["#Email", "#Fax", "#Phone"]); } } } , errorPlacement: function (error, element, fnUnobtrusive, form) { switch (element.attr("name")) { case "Email": case "Fax": case "Phone": onGroupError(error, "CheckBoxGroup", form); break; default: fnUnobtrusive(error, element); break; } } } function validateCheckboxGroup(names) { var result = true; $.each(names, function (index, value) { if ($(value).is(":checked")) { result = false; } }); return result; } 

Como el validador no intrusivo no implementa la configuración de grupos del validador de jquery, necesitamos reutilizar dos funciones de las dos bibliotecas para: (1) nombres de grupos divididos (reutilizando el código del validador de jquery) y (2) anexar elementos de error sin eliminar ‘input- clase validation-error ‘(reutilizando la función onError desde la biblioteca no intrusiva).

 function initGroups(validators) { validators.groups = {}; $.each(validators.settings.groups, function (key, value) { if (typeof value === "string") { value = value.split(/\s/); } $.each(value, function (index, name) { validators.groups[name] = key; }); }); } function onGroupError(error, inputElementName, form) { var container = $(form).find("[data-valmsg-for='" + inputElementName + "']"), replaceAttrValue = container.attr("data-valmsg-replace"), replace = replaceAttrValue ? $.parseJSON(replaceAttrValue) !== false : null; container.removeClass("field-validation-valid").addClass("field-validation-error"); error.data("unobtrusiveContainer", container); if (replace) { container.empty(); error.appendTo(container); } else { error.hide(); } } 

Por último, use HtmlExtensions.ValidationMessage para crear el intervalo de error del grupo de casillas de verificación.

 @Html.ValidationMessage("CheckBoxGroup", new { @class = "text-danger" }) 

El mantenimiento de la clase “input-validation-error” es necesario, de modo que el validador de jquery validará los 3 elementos (correo electrónico, teléfono, fax) del grupo de casillas como un todo, en lugar de validar uno por uno. La biblioteca de validación discreta elimina esta clase de forma predeterminada en la función onError, por lo que tenemos que personalizar esto como showne en la función onGroupError anterior.