with twitter-bootstrap asp.net-mvc-4 razor unobtrusive-validation

with - MVC4 Validación con razor y twitter bootstrap



twitter share button (5)

Necesito validar mi opinión en la maquinilla de afeitar MVC4. El problema es que quiero usar Twitter bootstrap y luego establecer la clase de mensaje de error para el div superior como se ve aquí: http://getbootstrap.com/css/#forms-control-states

¿Cómo puedo cambiar la clase de div principal?



La respuesta aceptada necesitaba modificaciones para trabajar con Bootstrap 3. He utilizado lo siguiente con éxito con MVC 4 y Bootstrap 3.

$(function () { // any validation summary items should be encapsulated by a class alert and alert-danger $(''.validation-summary-errors'').each(function () { $(this).addClass(''alert''); $(this).addClass(''alert-danger''); }); // update validation fields on submission of form $(''form'').submit(function () { if ($(this).valid()) { $(this).find(''div.control-group'').each(function () { if ($(this).find(''span.field-validation-error'').length == 0) { $(this).removeClass(''has-error''); $(this).addClass(''has-success''); } }); } else { $(this).find(''div.control-group'').each(function () { if ($(this).find(''span.field-validation-error'').length > 0) { $(this).removeClass(''has-success''); $(this).addClass(''has-error''); } }); $(''.validation-summary-errors'').each(function () { if ($(this).hasClass(''alert-danger'') == false) { $(this).addClass(''alert''); $(this).addClass(''alert-danger''); } }); } }); // check each form-group for errors on ready $(''form'').each(function () { $(this).find(''div.form-group'').each(function () { if ($(this).find(''span.field-validation-error'').length > 0) { $(this).addClass(''has-error''); } }); }); }); var page = function () { //Update the validator $.validator.setDefaults({ highlight: function (element) { $(element).closest(".form-group").addClass("has-error"); $(element).closest(".form-group").removeClass("has-success"); }, unhighlight: function (element) { $(element).closest(".form-group").removeClass("has-error"); $(element).closest(".form-group").addClass("has-success"); } }); }();


Me gusta agregar clases en el servidor, y no en el lado del cliente, por lo tanto, mi solución no tiene javascript (en mi caso, es un sistema simple con solo 2 o 3 campos en el formulario).

Esto es para usar la misma estructura de Form -> Validation States de la documentación de Bootstrap, disponible en http://getbootstrap.com/css/#forms-control-validation

Primero, creé un Html Helper para verificar si el ModelState es válido, y si tiene un error en el campo, devuelve una cadena (nombre de clase). Podría haber hecho esto directamente en la vista, pero me gusta una vista más limpia. Entonces, mi clase:

public static class ErrorHelper { private static string GetPropertyName<TModel, TValue>(Expression<Func<TModel, TValue>> expression) { // Get field name // Code from: https://.com/a/2916344/4794469 MemberExpression body = expression.Body as MemberExpression; if (body != null) return body.Member.Name; UnaryExpression ubody = expression.Body as UnaryExpression; if (ubody != null) body = ubody.Operand as MemberExpression; return body?.Member.Name; } public static MvcHtmlString ReturnOnError<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string @class) { var propertyName = GetPropertyName(expression); if (propertyName == null) return MvcHtmlString.Empty; if (htmlHelper.ViewData.ModelState.IsValid || !htmlHelper.ViewData.ModelState.ContainsKey(propertyName)) return MvcHtmlString.Empty; return htmlHelper.ViewData.ModelState[propertyName].Errors.Any() ? MvcHtmlString.Create(@class) : MvcHtmlString.Empty; } }

Importaciones:

using System; using System.Linq; using System.Linq.Expressions; using System.Web.Mvc;

Ahora, en mi vista, solo necesito usar la clase, como:

<div class="form-group @Html.ReturnOnError(m => m.Name, "has-error")"> @Html.LabelFor(m => m.Name, new { @class = "control-label" }) @Html.TextBoxFor(m => m.Name, new { @class = "form-control" }) @Html.ValidationMessageFor(m => m.Name, null, new { @class = "help-block" }) </div>


Modificar los valores predeterminados del validador:

// Override jquery validate plugin defaults in order to utilize Twitter Bootstrap 3 has-error, has-success, etc. styling. $.validator.setDefaults({ highlight: function(element) { $(element).closest(''.form-group'').addClass(''has-error'').removeClass(''has-success''); }, unhighlight: function(element) { $(element).closest(''.form-group'').removeClass(''has-error'').addClass(''has-success''); }, errorElement: ''span'', errorClass: ''help-block'', errorPlacement: function (error, element) { if (element.parent(''.input-group'').length || element.prop(''type'') === ''checkbox'' || element.prop(''type'') === ''radio'') { error.insertAfter(element.parent()); } else { error.insertAfter(element); } } });

Cree un nuevo resumen de validación parcial (para que tenga control total sobre el marcado):

@model ModelStateDictionary <div class="@(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") panel panel-danger" data-valmsg-summary="true"> <div class="panel-heading"> Please, correct the following errors: </div> <div class="panel-body"> <ul> @foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors)) { <li>@modelError.ErrorMessage</li> } </ul> </div> </div>

Agregue un estilo a su CSS (para ocultar el resumen de validación en la carga inicial sin tener que depender de las secuencias de comandos del lado del cliente para ocultarlo después del hecho):

.validation-summary-valid { display: none; }

Y renderice su resumen de validación alternativo en su vista en lugar de @ Html.ValidationSummary ():

@{Html.RenderPartial("_HtmlValidationSummary", ViewData.ModelState);}

Y estás listo para ir. Ah, y para obtener un estilo agradable para checkbox y radio, dependiendo de su configuración, el script anterior lo captará, pero lo más fácil es simplemente modificar su paridad Boolean.cshtml para que sea como la siguiente (donde Html.FormGroupFor es solo una versión ligeramente actualizada de https://github.com/erichexter/twitter.bootstrap.mvc/blob/master/src/Bootstrap/BootstrapSupport/ControlGroupExtensions.cs - simplemente cambie las referencias de "grupo de control" a "form-group" y añada "controlGroupWrapper.AddCssClass (" has-error ");" en la línea 58''ish):

@using Bctf.Web.HtmlHelpers @model bool? @using (Html.FormGroupFor(x => Model)) { <label class="checkbox"> @Html.CheckBox("", Model.HasValue && Model.Value, new { @class = "tickbox-single-line" }) @ViewData.ModelMetadata.DisplayName </label> @Html.ValidationMessageFor(x => Model, string.Empty, new { @class = "help-block" }) }

Todo lo anterior es una compilación de varias soluciones que he encontrado en muchos lugares diferentes que han hecho la combinación correcta. La solución de Ben anterior funciona, pero hace un poco de lado del cliente que provoca un jitter cada vez que se llama a algún método (es decir, publicación, carga de la página, error de validación). Lo anterior hace todo lo que necesita sin tener todo ese DOM en bucle.

Las diversas respuestas encontradas en Bootstrap 3 con jQuery Validation Plugin cubren gran parte de lo anterior (y el crédito por una porción significativa de lo anterior va a los diversos carteles allí, aunque ninguna respuesta lo cubre todo).


Solución

Incluir último:

$.validator.unobtrusive.options = { errorPlacement: function (error, element) { $(element).closest(".form-group").addClass("has-error"); } , success: function (label, element) { $(element).closest(".form-group").removeClass("has-error"); } }

Actualmente en jquery.validate.unobtrusive.js, el siguiente código muestra cómo MS permite extenderse:

options: { // options structure passed to jQuery Validate''s validate() method errorClass: defaultOptions.errorClass || "input-validation-error", errorElement: defaultOptions.errorElement || "span", errorPlacement: function () { onError.apply(form, arguments); execInContext("errorPlacement", arguments); }, invalidHandler: function () { onErrors.apply(form, arguments); execInContext("invalidHandler", arguments); }, messages: {}, rules: {}, success: function () { onSuccess.apply(form, arguments); execInContext("success", arguments); } }

Le permite básicamente anular / ampliar las siguientes opciones:

  • errorClass
  • errorElement
  • errrorPlacement
  • invalidHandler
  • éxito

Lo hace observando la variable global $.validator.unobtrusive.options (que no existe a menos que lo haga). defaultOptions se establece en $.validator.unobtrusive.options y execInContext es un método que encuentra el método aplicable en $.validator.unobtrusive.options y lo ejecuta con los argumentos originales (para que el método MS original no se destruya).