Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

18 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
lunes, 26 de abril de 2010
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?

Aún no hay comentarios, ¡sé el primero!