Maxlength en cuadros de texto de formularios MVC

ASP.NET MVC Resulta algo paradójico que ASP.NET MVC sea capaz de generar código para comprobar tanto en cliente como en servidor que la longitud del texto introducido sea menor que la indicada con la anotación [StringLength], y sin embargo, los helpers habituales no generen el atributo maxlength en el tag <input type="text">.

Podemos comprobarlo muy fácilmente. Por ejemplo, dada una entidad del Modelo como la siguiente:

public class Persona
{
  [StringLength(20, ErrorMessage="Máximo {1} caracteres")]
  public string Nombre { get; set; }
}
Si creamos su vista de edición por defecto e introducimos en ella los validadores en cliente, obtendremos en tiempo de ejecución un resultado como el que podemos apreciar en la siguiente captura de pantalla:
image

Es decir, los validadores automáticos, tanto en cliente como en servidor, nos avisan de que hemos superado el número máximo de caracteres a introducir, pero no se impide que esto se produzca mediante el atributo maxlength de la etiqueta el tag <input type="text">.

Afortunadamente hay varias formas de solucionar este pequeño inconveniente, como creando nuevos helpers o utilizando plantillas de edición personalizadas. En este post veremos cómo implementar la primera opción, creando un helper que realice la tarea de forma automática.

Html.LimitedTextBoxFor()

Vale, sé que no es un nombre estupendo para el helper, pero de momento va a tener que valer con ese ;-P. La idea es que podamos generar text boxes que limiten el número de caracteres permitidos en función de lo indicado en las anotaciones del Modelo. Es decir, sobre el ejemplo anterior, ocurriría lo siguiente:

// En la vista:
<%= Html.LimitedTextBoxFor(model => model.Nombre ) %>
 
// HTML resultante:
<input id="Nombre" maxlength="20" name="Nombre" type="text" value="" />

El código del helper es el siguiente, y lo comento brevemente justo a continuación:

public static MvcHtmlString LimitedTextBoxFor<TModel, TProperty>(
                this HtmlHelper<TModel> html, 
                Expression<Func<TModel, TProperty>> expression, 
                IDictionary<string, object> htmlAttributes)
{
  // Obtenemos los metadatos de la propiedad
  ModelMetadata metadata = 
         ModelMetadata.FromLambdaExpression(expression, html.ViewData);
 
  // Obtenemos la información utilizada para validar el tamaño del texto
  ControllerContext cctx = html.ViewContext.Controller.ControllerContext;
  StringLengthAttributeAdapter stringLengthValidator =
                    metadata.GetValidators(cctx)
                   .OfType<StringLengthAttributeAdapter>()
                   .FirstOrDefault();
 
  if (stringLengthValidator != null)
  {                                               // Si hay validación de este tipo...
    var parms = stringLengthValidator               
                     .GetClientValidationRules()     
                     .First()                        
                     .ValidationParameters;
                                                 // Obtenemos el valor del
    int maxlength = (int)parms["maximumLength"]; // tamaño máximo para el texto...
 
    if (htmlAttributes == null)
      htmlAttributes = new RouteValueDictionary();
 
    htmlAttributes.Add("maxlength", maxlength);  // y añadimos el atributo maxlength
  }
 
  return html.TextBoxFor(expression, htmlAttributes);
}

En primer lugar, estamos accediendo a los metadatos de la propiedad partiendo de la expresión lambda que recibimos como parámetro. Desde estos metadatos obtenemos, si existe, el validador asociado al atributo [StringLength], que es de tipo StringLengthAttributeAdapter.

Una vez obtenido el validador, buscamos en éste la información que será suministrada a los scripts en cliente, en particular el valor del parámetro “maximumLength”, introduciéndolo en la colección de atributos HTML que finalmente se envía al helper original asociando este valor al atributo maxlength de la etiqueta a generar.

Ahora, para simplificar las llamadas al helper desde las vistas, creamos un par de sobrecargas, similares a las que utilizamos normalmente en TextBoxFor():

public static MvcHtmlString LimitedTextBoxFor<TModel, TProperty>(
                  this HtmlHelper<TModel> html, 
                  Expression<Func<TModel, TProperty>> expression, 
                  object htmlAttributes)
{
  return html.LimitedTextBoxFor<TModel, TProperty>(
           expression, 
           ((IDictionary<string, object>)new RouteValueDictionary(htmlAttributes))
  );
}
 
public static MvcHtmlString LimitedTextBoxFor<TModel, TProperty>(
                  this HtmlHelper<TModel> html, 
                  Expression<Func<TModel, TProperty>> expression)
{
  return html.LimitedTextBoxFor(expression, null);
}

Y eso es todo. Si sustituimos en nuestras vistas las llamadas a TextBoxFor  por LimitedTextBoxFor tendremos la cuestión solucionada. Otra posibilidad, bastante más cómoda, sería retocar las plantillas de edición generadas por Visual Studio para que incluyan las llamadas a estos nuevos helpers.

Podéis encontrar el código fuente completo en Skydrive.

Publicado en: Variable not found.
Hey, ¿sabes que estoy en Twitter?

2 comentarios en “Maxlength en cuadros de texto de formularios MVC”

  1. Buenas!!
    Lo mejor del post, esa BLOCKED EXPRESSION que te ha salido por ahí, jejejeee… 🙂

    No, ahora en serio, genial post!

    Precisamente (y cambiando un poco de tema, pero así aprovecho el comentario) esto de los helpers es algo que, creo, no terminan de tener bien resuelto en la distribución de ASP.NET MVC. Para mi todos los métodos extensores sobre los helpers deberían distribuirse en assemblies separados. Eso permitiría decidir que métodos quiero en cada momento. P.ej. yo siempre uso jQuery y nunca la ajax library de MS, y bueno… tengo que “tragarme” todos los métodos extensores sobre AjaxHelper que me generan código para la Ajax Library y que no quiero para nada… Con lo fácil que seria que estuvieran en un assmbly separado y que los incluyese quien quisiera, no???

    Un saludo!

  2. Gracias, Eduard, no me había dado cuenta! Se ve que a Community Server no le ha gustado esa porción de código y la bloquea. 🙁

    Bueno, al que le interese el tema, puede acudir a Variable not found (http://www.variablenotfound.com/2010/04/maxlength-en-cuadros-de-texto-de.html) y verlo allí o descargar el fuente desde skydrive.

    Por otra parte, respecto a lo que comentas de los extensores, sabrás que puedes “ocultarlos” eliminando las referencias a los espacios de nombre por defecto en el web.config, o incluyendo otras. Es el mecanismo que idearon para hacer lo que comentas, que cada uno pudiera implementar sus propias extensiones… en fin, menos da una piedra.

    Saludos y gracias de nuevo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *