Autor en Google+
Saltar al contenido

Variable not found. Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo de software, internet, u otros temas relacionados con la tecnología. C#, ASP.NET, ASP.NET MVC, HTML, Javascript, CSS, jQuery, Ajax, VB.NET, componentes, herramientas...

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, noticias, curiosidades, reflexiones... sobre el mundo del desarrollo
de software, internet, u otros temas relacionados con la tecnología

¡Microsoft MVP!
miércoles, 14 de diciembre de 2011
ASP.NET MVCUno de los aspectos más criticados por los desarrolladores cuando comienzan a trabajar con ASP.NET MVC es el hecho de tener que volver a resolver problemas que estaban ya más que solucionados en Webforms.

Y uno de estos casos es un detallito muy simple pero útil: establecer el foco de edición en un control concreto al cargar una página. En Webforms era suficiente con asignar al atributo defaultFocus del tag <form> el nombre del control que nos interesara, y ya lo teníamos solucionado; ASP.NET MVC no trae ninguna solución “de serie” para conseguirlo, aunque, como veremos en este post, no es mucho más complicado que la alternativa Webforms una vez hemos preparado la infraestructura necesaria.

En líneas generales, la solución consiste en utilizar un pequeño script que, una vez cargada la página, mueva el foco hacia el control que nos interese. Por ejemplo, si quisiéramos que el campo con identificador “Nombre” obtenga el foco automáticamente, podríamos utilizar jQuery de la siguiente forma:
<script type="text/javascript">
    $(function () {
        $("#Nombre").focus();
    });
</script>
Aunque eficaz, es una solución demasiado trabajosa e introduce una dependencia en el código, difícilmente manejable, hacia el nombre del control. Si quisiéramos introducir este código en múltiples puntos de nuestra aplicación tendríamos que copiar y pegar en cada vista, y sustituir el id del control apropiado en cada caso, lo cual es bastante incómodo.

¿No podríamos mejorar esto un poco?

El helper SetFocusTo()

Una posible solución, realmente rápida de implementar, sería crear un helper personalizado, al que llamaremos SetFocusTo(),  que genere el código por nosotros partiendo únicamente de la información de la propiedad a editar.

Y para evitar la dependencia hacia el nombre de la propiedad y hacerlo más flexible, incluso podríamos crear la versión fuertemente tipada del mismo, de forma que podamos utilizarlo de cualquiera de las siguientes formas:
// Usando el nombre de la propiedad:
@Html.SetFocusTo("Nombre")

// Usando la versión fuertemente tipada:
@Html.SetFocusTo(model=>model.Nombre)
El código del helper es el siguiente:
public static class HtmlHelpers
{
    public static MvcHtmlString SetFocusTo<TModel, TProperty>(
                    this HtmlHelper<TModel> html,
                    Expression<Func<TModel, TProperty>> expression)
    {
        var prop = ExpressionHelper.GetExpressionText(expression);
        return html.setFocusTo(prop);
    }

    public static MvcHtmlString SetFocusTo(this HtmlHelper html, 
                                           string propertyName)
    {
        var prop = ExpressionHelper.GetExpressionText(propertyName);
        return html.setFocusTo(prop);
    }

    private static MvcHtmlString setFocusTo(this HtmlHelper html, 
                                            string property)
    {
        string id = html.ViewData.TemplateInfo.GetFullHtmlFieldId(property);
        var script = "<script type='text/javascript'>" +
                        "$(function() {" +
                            "$('#" + id + "').focus();" +
                        "});" +
                        "</script>";
        return MvcHtmlString.Create(script);
    }

}
Recordad que para que estos helpers estén visible en las vistas y podáis usarlos normalmente, debéis incluir el espacio de nombres en el que están definidos bien sea añadiendo una directiva @using en la vista, o bien en la sección <namespaces> del archivo web.config que encontraréis en la carpeta /Views (o en la del área correspondiente).

¡Y eso es todo! De esta forma tan simple, ya volvemos a disponer de la posibilidad de establecer el foco por defecto sobre un control de forma muy rápida, compacta, y cómoda de utilizar.

Publicado en: Variable not found.

Estos contenidos se publican bajo una licencia de Creative Commons Licencia Reconocimiento-No comercial-Compartir bajo la misma licencia 3.0 España de Creative Commons

8 Comentarios:

Juan Carlos dijo...

Gracias por el aporte

José M. Aguilar dijo...

Un placer! ;-)

Saludos & gracias por comentar.

MelDJ dijo...

Muy bien. Gracias.

José M. Aguilar dijo...

Gracias a tí, @MelDJ, por comentar!

Salvador Anaya dijo...

Muy bueno, sencillo pero eficaz!! Gracias!

José M. Aguilar dijo...

Muchas gracias, Salvador :-)

Paco dijo...

Hola José María, gracias por mostrar estos ejemplos, vienen genial.

¿sabes si se podría conseguir esto mismo de forma declarativa, por ejemplo con atributos sobre las clases del modelo? Ej:

[Focus]
public string Nombre

Gracias por todo.

José M. Aguilar dijo...

Hola, Paco!

Ante todo, gracias por comentar.

Hombre, poderse se podría, aunque no sé si es buena idea hacerlo tal cual. Es decir, de alguna forma, estarías salpicando el modelo con una particularidad de la vista, que es el establecimiento del foco inicial... aunque por otro lado, también ocurre con otros atributos.

Bueno, filosofía aparte, en cualquier caso es técnicamente posible conseguirlo. Un enfoque podría ser usando el sistema de validaciones (aunque de forma algo artificial) y otro, probablemente más razonable, usando metadatos del modelo; precisamente hace poco he publicado un post hablando de IMetadataAware que podría valer.

En cualquier caso, si te parece publico un post en unos días mostrando la solución completa, ok?

Un saludo!