errormessage - atributo maxlength de un cuadro de texto de DataAnnotations StringLength en Asp.Net MVC
mvc required field validation (7)
Aquí hay algunos métodos estáticos que puede usar 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
Estoy trabajando en una aplicación MVC2 y quiero establecer los atributos de longitud máxima de las entradas de texto.
Ya he definido el atributo de longitud de cuerda en el objeto Modelo usando anotaciones de datos y está validando la longitud de las cadenas ingresadas correctamente.
No quiero repetir la misma configuración en mis vistas configurando el atributo de longitud máxima manualmente cuando el modelo ya tiene la información. ¿Hay alguna manera de hacer esto?
Fragmentos de código a continuación:
Del 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 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 manera de hacer esto?
Aunque personalmente estoy adorando el jquery fix de jrummel, aquí hay otro enfoque para mantener una única fuente de verdad en tu modelo ...
No es lindo, pero ... funcionó bien para mí ...
En lugar de usar decoraciones de propiedades, simplemente 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 los atributos Html, por ej.
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 afeitar, en el Editor Para ... use un objeto HtmlAttirubte en la sobrecarga, proporcione la propiedad deseada de longitud máxima y referenece la constante ... deberá suministrar la constante a través de una ruta de espacio de nombres totalmente calificada. .. MyCompany.MyModel.MAX_ZIPCODE_LENGTH ... ya que no estará colgado directamente del modelo, pero funciona.
Considero 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 de mvc editor / display templates (donde TModel
termina siendo un tipo simple como string
).
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);
}
No conozco ninguna forma de lograr esto sin recurrir a la reflexión. Puedes escribir un método de ayuda:
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 puedes usar así:
<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%>
Si desea que esto funcione con una clase de metadatos, debe usar el siguiente código. Sé que no es bonita, pero hace el trabajo y evita que tenga que escribir sus 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; }
/* ... */
}
}
Si está utilizando una validación discreta, puede manejar este lado del cliente también:
$(document).ready(function ()
{
$("input[data-val-length-max]").each(function ()
{
var $this = $(this);
var data = $this.data();
$this.attr("maxlength", data.valLengthMax);
});
});
Uso el CustomModelMetaDataProvider para lograr esto
Paso 1. Añadir 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 el CustomModelMetadataProvider
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new CustomModelMetadataProvider();
}
Paso 3. En Views / Shared / EditorTemplates Agrega 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 elementos 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)%>