2010-03-05 8 views
75

Estoy trabajando en una aplicación MVC2 y quiero establecer los atributos de maxlength de las entradas de texto.maxlength atributo de un cuadro de texto de DataAnnotations StringLength en Asp.Net MVC

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?

Los fragmentos de código siguientes:

a partir 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 hacer es:

<%= Html.TextBoxFor(model => model.Address1, new { @class = "text long", maxlength="50" })%> 

I quiero obtener esta salida:

<input type="text" name="Address1" maxlength="50" class="text long"/> 

¿Hay alguna manera de hacerlo?

+0

Disculpa, no sé para qué sirve Data Annonations? Quiero decir, ¿y si el criterio de longitud cambia? ¿No se puede impulsar de forma dinámica (en tiempo de ejecución), en función de algunos metadatos? – shahkalpesh

Respuesta

50

No conozco ninguna forma de lograr esto sin recurrir a la reflexión. Se puede 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 se podría utilizar como esto:

<%= Html.CustomTextBoxFor(model => model.Address1, new { @class = "text long" })%> 
+0

Recibo Error 'System.Web.Mvc.HtmlHelper ' no contiene una definición para 'TextBoxFor' y ningún método de extensión 'TextBoxFor' acepta un primer argumento de tipo 'System.Web.Mvc.HtmlHelper 'podría encontrarse (¿falta una directiva using o una referencia de ensamblado?) en esta línea: return htmlHelper.TextBoxFor (expresión, atributos); – sabbour

+1

'usando System.Web.Mvc.Html'? –

+2

Sí, ya lo descubrí :) Tengo otro problema, mis anotaciones de datos están definidas en las clases de MetaData en lugar del propio modelo. ¡El reflejo no los está recogiendo! – sabbour

8

Si quieres que esto funcione con una clase de metadatos es necesario utilizar el siguiente código. Sé que no es bonito pero hace el trabajo y le impide tener que escribir sus propiedades maxlength tanto en la clase de entidad y 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 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; } 

    /* ... */ 
    } 
} 
20

Uso el CustomModelMetaDataProvider para lograr esto

Paso 1. Agregue la 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 el Registro Global.asax CustomModelMetadataProvider

protected void Application_Start() 
{ 
    AreaRegistration.RegisterAllAreas(); 
    RegisterRoutes(RouteTable.Routes); 
    ModelMetadataProviders.Current = new CustomModelMetadataProvider(); 
} 

Paso 3. En Vistas/Común/EditorTemplates Añadir 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 su caso, puede 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)%> 
54

Si está utilizando la validación discreto que podrá manejar este lado del cliente, así:

$(document).ready(function() 
{ 
    $("input[data-val-length-max]").each(function() 
    { 
     var $this = $(this); 
     var data = $this.data(); 
     $this.attr("maxlength", data.valLengthMax); 
    }); 
}); 
+0

Si bien su enfoque me daría validación, en realidad estaba después de poner el atributo maxlength en la entrada porque evitaría que el usuario ingrese más caracteres en el navegador y funcionaría independientemente de si javascript se ejecutaba en el navegador. –

+5

Eso es exactamente lo que hace esto. Utiliza el atributo de validación de longitud máxima de datos para establecer el atributo de entrada maxlenth. – jrummell

+5

Estaba realmente entusiasmado con la primera respuesta de reflexión, pero esto busca obtener los mismos resultados sin ningún código de servidor complejo. Buen trabajo. Deberías obtener más votos. –

3

Mientras estoy personalmente de amor de jrummel jquery fix, aquí hay otro enfoque para mantener una única fuente de verdad en su modelo ...

No es bonita, pero ... ha funcionado bien para mí ...

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 los atributos Html, 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 

A continuación, en el archivo de vista de afeitar, en el EditorFor ... usar un objeto en el HtmlAttirubte sobrecarga en la alimentación de la propiedad max-longitud deseada y referenece la constante .. tendrá que suministrar la constante a través de una ruta de espacio de nombres totalmente calificada ... MyCompany.MyModel.MAX_ZIPCODE_LENGTH ... ya que no se quedará directamente fuera del modelo, pero funciona.

1

Encontré que el enfoque basado en la reflexión de Darin era 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); 
} 
1

Aquí hay 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); 
} 

} 

Utilizando el método estático ...

var length = AttributeHelpers.GetStringLength<User>(x => x.Address1); 

o utilizando el método de extensión opcional en una instancia ...

var player = new User(); 
var length = player.GetStringLength(x => x.Address1); 

o utilizando 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

Cuestiones relacionadas