Estoy trabajando en una aplicación MVC2 y quiero establecer los atributos de longitud máxima de las entradas de texto.
Ya definí el atributo stringlength en el objeto Model usando anotaciones de datos y está validando la longitud de las cadenas ingresadas correctamente.
No quiero repetir la misma configuración en mis vistas estableciendo el atributo de longitud máxima manualmente cuando el modelo ya tiene la información. ¿Hay alguna forma de hacer esto?
Fragmentos de código a continuación:
Desde el modelo:
[Required, StringLength(50)]
public string Address1 { get; set; }
Desde la vista:
<%= Html.LabelFor(model => model.Address1) %>
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long" })%>
<%= Html.ValidationMessageFor(model => model.Address1) %>
Lo que quiero evitar hacer es:
<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long", maxlength="50" })%>
Quiero obtener esta salida:
<input type="text" name="Address1" maxlength="50" class="text long"/>
¿Hay alguna forma de hacer esto?
asp.net-mvc
validation
Pervez Choudhury
fuente
fuente
Respuestas:
No conozco ninguna forma de lograrlo sin recurrir a la reflexión. Podrías escribir un método auxiliar:
public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes ) { var member = expression.Body as MemberExpression; var stringLength = member.Member .GetCustomAttributes(typeof(StringLengthAttribute), false) .FirstOrDefault() as StringLengthAttribute; var attributes = (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes); if (stringLength != null) { attributes.Add("maxlength", stringLength.MaximumLength); } return htmlHelper.TextBoxFor(expression, attributes); }
que podrías usar así:
<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%>
fuente
Si está utilizando una validación discreta, también puede manejar este lado del cliente:
$(document).ready(function () { $("input[data-val-length-max]").each(function () { var $this = $(this); var data = $this.data(); $this.attr("maxlength", data.valLengthMax); }); });
fuente
Utilizo CustomModelMetaDataProvider para lograr esto
Paso 1. Agregar nueva clase CustomModelMetadataProvider
public class CustomModelMetadataProvider : DataAnnotationsModelMetadataProvider { protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { ModelMetadata metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); //Add MaximumLength to metadata.AdditionalValues collection var stringLengthAttribute = attributes.OfType<StringLengthAttribute>().FirstOrDefault(); if (stringLengthAttribute != null) metadata.AdditionalValues.Add("MaxLength", stringLengthAttribute.MaximumLength); return metadata; } }
Paso 2. En Global.asax, registre CustomModelMetadataProvider
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); RegisterRoutes(RouteTable.Routes); ModelMetadataProviders.Current = new CustomModelMetadataProvider(); }
Paso 3. En Vistas / Shared / EditorTemplates, agregue una vista parcial llamada String.ascx
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <%if (!ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) { %> <%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line" }) %> <% } else { int maxLength = (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"]; %> <%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { @class = "text-box single-line", MaxLength = maxLength })%> <% } %>
Hecho...
Editar. El Paso 3 puede comenzar a ponerse feo si desea agregar más cosas al cuadro de texto. Si este es tu caso puedes hacer lo siguiente:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %> <% IDictionary<string, object> Attributes = new Dictionary<string, object>(); if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("MaxLength")) { Attributes.Add("MaxLength", (int)ViewData.ModelMetadata.AdditionalValues["MaxLength"]); } if (ViewData.ContainsKey("style")) { Attributes.Add("style", (string)ViewData["style"]); } if (ViewData.ContainsKey("title")) { Attributes.Add("title", (string)ViewData["title"]); } %> <%: Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, Attributes)%>
fuente
Si desea que esto funcione con una clase de metadatos, debe usar el siguiente código. Sé que no es bonito, pero hace el trabajo y evita que tengas que escribir tus propiedades de longitud máxima tanto en la clase Entidad como en la Vista:
public static MvcHtmlString TextBoxFor2<TModel, TProperty> ( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes = null ) { var member = expression.Body as MemberExpression; MetadataTypeAttribute metadataTypeAttr = member.Member.ReflectedType .GetCustomAttributes(typeof(MetadataTypeAttribute), false) .FirstOrDefault() as MetadataTypeAttribute; IDictionary<string, object> htmlAttr = null; if(metadataTypeAttr != null) { var stringLength = metadataTypeAttr.MetadataClassType .GetProperty(member.Member.Name) .GetCustomAttributes(typeof(StringLengthAttribute), false) .FirstOrDefault() as StringLengthAttribute; if (stringLength != null) { htmlAttr = new RouteValueDictionary(htmlAttributes); htmlAttr.Add("maxlength", stringLength.MaximumLength); } } return htmlHelper.TextBoxFor(expression, htmlAttr); }
Clase de ejemplo :
[MetadataType(typeof(Person.Metadata))] public partial class Person { public sealed class Metadata { [DisplayName("First Name")] [StringLength(30, ErrorMessage = "Field [First Name] cannot exceed 30 characters")] [Required(ErrorMessage = "Field [First Name] is required")] public object FirstName { get; set; } /* ... */ } }
fuente
Si bien personalmente me encanta la solución jquery de jrummel, aquí hay otro enfoque para mantener una única fuente de verdad en su modelo ...
No es bonito, pero ... me ha funcionado bien ...
En lugar de usar decoraciones de propiedad, solo defino algunas constantes públicas bien nombradas en mi biblioteca de modelos / dll, y luego las hago referencia en mi vista a través de HtmlAttributes, por ejemplo
Public Class MyModel Public Const MAX_ZIPCODE_LENGTH As Integer = 5 Public Property Address1 As String Public Property Address2 As String <MaxLength(MAX_ZIPCODE_LENGTH)> Public Property ZipCode As String Public Property FavoriteColor As System.Drawing.Color End Class
Luego, en el archivo de vista de maquinilla de afeitar, en EditorFor ... use un objeto HtmlAttirubte en la sobrecarga, proporcione la propiedad de longitud máxima deseada y haga referencia a la constante ... tendrá que proporcionar la constante a través de una ruta de espacio de nombres completamente calificada. .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH .. ya que no estará colgando directamente del modelo, pero funciona.
fuente
Encontré que el enfoque basado en la reflexión de Darin es especialmente útil. Descubrí que era un poco más confiable usar los metadatos
ContainerType
como base para obtener la información de la propiedad, ya que este método puede ser llamado dentro del editor de mvc / plantillas de visualización (dondeTModel
termina siendo un tipo simple comostring
).public static MvcHtmlString CustomTextBoxFor<TModel, TProperty>( this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes ) { var metadata = ModelMetadata.FromLambdaExpression( expression, new ViewDataDictionary<TModel>( htmlHelper.ViewDataContainer.ViewData ) ); var stringLength = metadata.ContainerType.GetProperty(metadata.PropertyName) .GetCustomAttributes(typeof(StringLengthAttribute), false) .FirstOrDefault() as StringLengthAttribute; var attributes = (IDictionary<string, object>)new RouteValueDictionary(htmlAttributes); if (stringLength != null) { attributes.Add("maxlength", stringLength.MaximumLength); } return htmlHelper.TextBoxFor(expression, attributes); }
fuente
A continuación se muestran algunos métodos estáticos que puede utilizar para obtener StringLength o cualquier otro atributo.
using System; using System.Linq; using System.Reflection; using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; public static class AttributeHelpers { public static Int32 GetStringLength<T>(Expression<Func<T,string>> propertyExpression) { return GetPropertyAttributeValue<T,string,StringLengthAttribute,Int32>(propertyExpression,attr => attr.Length); } //Optional Extension method public static Int32 GetStringLength<T>(this T instance,Expression<Func<T,string>> propertyExpression) { return GetStringLength<T>(propertyExpression); } //Required generic method to get any property attribute from any class public static TValue GetPropertyAttributeValue<T, TOut, TAttribute, TValue>(Expression<Func<T,TOut>> propertyExpression,Func<TAttribute,TValue> valueSelector) where TAttribute : Attribute { var expression = (MemberExpression)propertyExpression.Body; var propertyInfo = (PropertyInfo)expression.Member; var attr = propertyInfo.GetCustomAttributes(typeof(TAttribute),true).FirstOrDefault() as TAttribute; if (attr==null) { throw new MissingMemberException(typeof(T).Name+"."+propertyInfo.Name,typeof(TAttribute).Name); } return valueSelector(attr); } }
Usando el método estático ...
var length = AttributeHelpers.GetStringLength<User>(x => x.Address1);
O usando el método de extensión opcional en una instancia ...
var player = new User(); var length = player.GetStringLength(x => x.Address1);
O usando el método estático completo para cualquier otro atributo ...
var length = AttributeHelpers.GetPropertyAttributeValue<User,string,StringLengthAttribute,Int32>(prop => prop.Address1,attr => attr.MaximumLength);
Inspirado por la respuesta aquí ... https://stackoverflow.com/a/32501356/324479
fuente