June 2011 - Artículos

Buenas! La verdad es que llevo algunos días sin actualizar mucho el blog… Ya se sabe trabajo y tal :)

Hoy quiero comentaros algo rapidito y que se ha preguntado varias veces en los foros y que es como poder asignar un ID al <label /> generado por el helper Html.LabelFor<T>. En este caso vamos a hacer que se le puedan añadir todos los atributos que se quieran a la etiqueta <label />

Aunque use este helper, la técnica aplicada debería serviros para ver como ampliar los helpers, en caso que lo necesitéis.

Por ejemplo, dado un viewmodel que tenga una propiedad Address el siguiente código:

@Html.LabelFor(x => x.Address);

Nos genera el HTML siguiente:

<label for="Address">Address</label>

Si quisiéramos parametrizar el <label /> generado nos encontramos conque ninguna de las dos sobrecargas que ofrece el helper nos es de ayuda (la otra simplemente nos permite especificar el texto de la cadena).

En nuestro caso queremos poder hacer una llamada como la siguiente:

@Html.LabelFor(x => x.Address, new { id = "lblEiximenis", width = "100" })<br />

y que el código HTML generado sea:

<label for="Address" id="lblEiximenis" width="100">Address</label>

Bien! Por suerte es muy sencillito, basta con crearnos un método extensor sobre HtmlHelper como el que sigue:

public static class HtmlExtensions
{
        public static MvcHtmlString LabelFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression, object htmlAttributes)
        {
            ModelMetadata meta = ModelMetadata.FromLambdaExpression (expression, html.ViewData);
            var htmlFullName = ExpressionHelper.GetExpressionText(expression);
            var labelText = meta.DisplayName ?? meta.PropertyName;
            if (String.IsNullOrEmpty(labelText))
            {
                return MvcHtmlString.Empty;
            }
            var tag = new TagBuilder("label");
            tag.Attributes.Add("for", html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldId(htmlFullName));
            if (htmlAttributes != null)
            {
                tag.MergeAttributes(new RouteValueDictionary(htmlAttributes));
            }
            tag.SetInnerText(labelText);
            return MvcHtmlString.Create(tag.ToString(TagRenderMode.Normal));
        }

    }

La verdad es que el código es muy sencillito, hay 3 puntos a destacar:

  1. El uso de ModelMetadata para de esta manera poder cojer el valor de los meta datos del modelo (usualmente las Data Annotations) y de este modo poder hacer caso de ellas. En este caso respetaríamos el atrbuto [DisplayName] si el usuario lo usara.
  2. El uso de ExpressionHelper.GetExpressionText. Este método lo que devuelve es el texto de una expresión lambda. Es decir, si yo tengo x=>x.Customer.Name, este método me devolverá “Customer.Name”. Esto lo necesitamos porque el valor del atributo for es todo el texto del cuerpo de la lambda expression. Si al atributo for le pasase el nombre de la propiedad en lugar de todo el texto de la expresión lambda no podría usar propiedades anidadas (es decir me funcionaría para x=>x.Name pero NO para x => x.Customer.Name).
  3. El método MergeAttributes del TagBuilder, que lo que hace es añadir a la etiqueta que se está construyendo todos aquellos atributos basándose en el IDictionary<string, object> que recibe como parámetro. Como en nuestro método los atributos los pasamos como objeto anónimo, nos aprovechamos de la clase RouteValueDictionary (que tiene un constructor que acepta un object). Eso nos evita tener que usar reflection directamente ;-)

Y listos! Ya podemos personalizar al máximo las <label>s generadas!

Un saludo a todos!

PD: El código de este méotdo está copiado casi directamente del código fuente de ASP.NET MVC. Así que el consejo final de este post es: mirad el código fuente de ASP.NET MVC aprendereis mucho de él.

PD2: Una búsqueda en Google me rebela que Imran Baloch se me ha avanzado por algunos días: http://weblogs.asp.net/imranbaloch/archive/2010/07/03/asp-net-mvc-labelfor-helper-with-htmlattributes.aspx

con no comments
Archivado en: