ASP.NET MVC 5 – Bootstrap en los templates de MVC

Otra de las características que trae ASP.NET MVC 5 es que las plantillas de proyectos utilizan por default Twitter Bootstrap  – actualmente en la versión 3.0.0 – como framework para nuestras interfaces de usuario.

Bootstrap, para quienes no lo conozcan, es un User Interface Framework open source desarrollado y mantenido para el equipo de desarrollo de Twitter. Este marco de trabajo nos permite construir, bajo un sistema de cuadrillas y CSS3, páginas responsivas. El diseño  web responsivo establece tener una única interfaz de usuario que se ajusta-alinea automáticamente a las distintas resoluciones de los distintos dispositivos (PCs, tablets, telefonos…). Además nos proporciona de estilos y recursos que hacen que nuestras páginas luzcan más modernas, atractivas y compatibles con las mayorías de los navegadores web.

Página creada con Boostrapt

Página creada con Boostrapt

Si prestan atención, cuando generamos un nuevo proyecto ASP.NET MVC 5 en VS2013 entre los scripts JS está incluida la librería de Bootstrap:

Librerías Boostrapt

Librerías Boostrapt

También están definidos los bundles correspondientes- archivo BundleConfig.cs – tanto para las librerías JS como para la hoja de estilo CSS:

Bundles de Boostrapt

Bundles de Boostrapt

Y por último las referencias en la vista _Layout.cshtml:

Referenciando a Boostrapt desde las vistas

Referenciando a Boostrapt desde las vistas

Otro aspecto importante es que todas las vistas generan por defecto código basado en este framework:

El código de las vistas basado en Boostrapt

El código de las vistas basado en Boostrapt

Si ejecutamos la aplicación y ajustamos el tamaño del navegador a una resolución acorde a la de un dispositivo móvil veremos como actúa el diseño responsivo (todo el contenido se ajusta en la pantalla y el menú superior es agrupado dentro de un icono que se expande al hacer click):

Diseño responsivo en acción

Diseño responsivo en acción

Para que quede claro la intención del post no es enseñarles a trabajar con Boostratp sino comentarles la adopción de este como framework UI por defualt. Si quieren conocer más pueden hacerlo desde aquí.

Antes de finalizar, es importante aclarar que la utilización de Bootstrap es solo una sugerencia (particularmente lo recomiendo), pero es posible utilizar cualquier otra librería o simplemente no utilizar ninguna.

Quienes deseen más de info sobre el tema les recomiendo este artículo de Tom Dykstra y este artículo de mi amigo Julio Avellaneda.

Espero que les sea de utilidad!

Anuncios

Introducción a Knockout

Knockout es una pequeña biblioteca JavaScript  de código abierto (40 kb de tamaño) escrita por Steve Sanderson (quien trabajo para Microsoft y es el autor del libro Pro ASP.NET MVC Framework ente otras muchas otras publicaciones).

Esta librería nos ayuda a crear interfaces ricas, responsivas y declarativas permitiendo enlazar-sincronizar objetos Javascripts con elementos HTML utilizando para ello el patrón MVVM. Esto nos permite, entre otras cosas, tener el front-end mucho más organizado y en consecuencia un código más legible y mantenible.

Knockout

Knockout

Ahora bien, de que se trata esto de enlazar elementos HTML con objetos de javascript?. Bien para apreciar todo el potencial de Knockout antes vamos a trabajar en un ejemplo muy simple utilizando la ya famosa librería jQuery. La idea es mostrar información sobre bloggeros en la página.

Arranquemos definiendo la página HTML:

HTML -Estructura inicial

HTML -Estructura inicial

Es importante en este punto agregar la referencia a la librería de jQuery. Sin embargo en este punto no es necesario agregar la librería a KO. También agregué la referencia al archivo de script – Index.js – donde vamos a definir toda la lógica del lado del cliente.

Y ahora el turno del JS:

JS - "Enlazando" con jQuery objetos JS con elementos HTML.

JS – “Enlazando” con jQuery objetos JS y elementos HTML.

Pasemos a revisar lo que hicimos. En primer lugar creamos un objeto javascript y lo asignamos a la variable persona. En segundo lugar “enlazamos” (aunque vamos a ver que no es del todo correcta esta definición en esta instancia) cada propiedad de la persona con los elementos HTML correspondientes (utilizando selectores de jQuery para identificar dichos elementos).

Simple, no?… pero pensemos un poco… trabajar de esta forma implica que vamos a necesitar una línea de código (sentencia de jQuery) por cada nueva propiedad que queramos enlazar al HTML. Es posible que en el corto plazo surjan nuevas propiedades de la persona que nos interese mostrar, por lo este código se puede llegar a tornar engorroso y  molesto, no?

Bien, unas líneas más arriba les comentaba que el término “enlace” no era del todo correcto. Supongamos que modificamos alguna de las propiedades de la persona: el valor se actualizará en el objeto HTML? Y sí modificamos el contenido del objeto HTML, en la propiedad de la persona se verá reflejado tal cambio? En ambos casos la respuesta es NO (al menos no de una manera simple y prolija).

Es el turno de que entre en escena Knockout (KO) y nos solucione el problema (aplausos!)

Arranquemos adaptando el HTML (ahora sí es necesaria la referencia a las librería KO, pueden descargarla desde acá):

HTML - Bindings declarativos

HTML – Bindings declarativos

Podrán observar que los atributos Id ya no son necesarios y en su lugar agregamos atributos data-bind, los cuales nos permitirán enlazar los datos entre los elementos destino y el objeto de origen. Estos atributos forman parte del sistema de bindings declarativos que proporciona KO.

JS - Enlanzado datos con KO.

JS – Enlazando datos con KO.

Un punto importante es comentar que el espacio de nombres de Knockout es ko (lo equivalente al $ de jQuery). Al llamar a la función applyBindings(model) Knockout enlaza el objeto especificado con los elementos de la página que tengan el atributo data-bind correspondiente. Como mencionamos arriba, esto forma parte del sistema de binding declarativos que nos permiten crear vínculos uni o bidireccionales entre elementos HTML y las propiedades o acciones del modelo javascript.

En los atributos data-bing podemos especificar  de que forma de enlazar los valores (en nuestro ejemplo hemos especificado enlace de textos y de valor ). Existen varios tipos de enlaces integrados proporcionados por KO que nos facilitan el enlace entre las propiedades de los objetos JS con los elementos del DOM:

  • “text: propiedad”: tipo de enlaces de texto (utilizados en elementos p y span).
  • “visible: propiedad”: tipos de enlaces de visibilidad (valores posibles true o false).
  • “value: propiedad”: tipo de enlaces de valor (utilizados en elementos input, select y textarea).
  • “css: propiedad”: tipo de enlaces de estilo (aplica al css de los elementos).
  • “checked: propiedad”: tipo de enlaces de ckeck (utilizado en elementos checkbox).
  • “click: propiedad”: tipo de enlace a eventos javascript (utilizado en elementos button, input y a).
  • “attr: {src: propiedadUrl, alt: propiedadTexto}”: tipo de enlace para enlaces (se utiliza en elementos img).

La forma en que KO aplica los enlaces integrado en los elementos de DOM variará entre otras cosas del tipo y versión del navegador.

Ahora bien, vamos a hacer algunos unos pequeños cambios en nuestro código. En primer lugar vamos a agregar un elemento <span> donde visualizar el nombre de la persona-blogger:

HTML - Agregando elemento span para visualizar nombre.

HTML – Agregando elemento span para visualizar nombre.

Y vamos a cambiar el nombre de la variable por algo más representativo al patrón que estamos aplicando:

JS - Renombrando al modelo.

JS – Renombrando al modelo.

Abramos la página:

Página Web v1.0

Página Web v1.0

Qué pasa si ahora cambiamos el nombre de la persona:

v1.0 no se comporta como esperamos

v1.0 no se comporta como esperamos

Mmmm… si ambos elementos HTML están enlazados a la misma propiedad del modelo,por qué no se actualizo el nombre del blogger en el mensaje de bienvenida cuando los modificamos en el campo de texto?

Para poder mantener sincronizados todos los elementos con el modelo KO utiliza el concepto de observables. Los observables permiten el seguimiento de las dependencias y son objetos que pueden notificar a unos agentes de escucha cuando los valores cambian.  Para definir una propiedad como observable simplemente debemos asignarle la función ko.observable(default_value).

Los observables pueden aplicarse a todas las propiedades de un modelo, o solo a las que nos interesa tener sincronizados:

JS - Definiendo propiedades como observables.

JS – Definiendo propiedades como observables.

Ahora si cambiamos el valor desde la caja de texto vamos a observar que cambia el mensaje de bienvenida también:

Página Web v2.0

Página Web v2.0

Esta característica se la conoce como seguimiento de dependencias y permite detectar los cambios que ocurren en la vista (elementos del DOM) como en el modelo-vista (objetos javascripts) para propagarlos hacia todos los objetos/elementos dependientes.

Pero aún hay más, que pasa si queremos adaptar el mensaje de bienvenida con el siguiente formato: “Bienvenido Nombre, felicitaciones por su blog Blog!!!”. Una solución muy simple sería agregar un nuevo elemento <span> y enlazarlo a la propiedad blog por medio de un observable. El problema es que cuanto más datos querramos visualizar en el mensaje, más elementos span deberíamos agregar, y en este punto nos volvemos a encontrar con un problema que deberíamos evitar con KO.

Por suerte KO nos provee de observables calculados.

Veamos como utilizarlos, modificando la estructura del documento HTML:

HTML - Creamos un elemento donde enlazar el mensaje.

HTML – Creamos un elemento donde enlazar el mensaje.

Acto seguido, modificamos el script JS:

JS - Enlazando por medio de un observable calculado.

JS – Enlazando por medio de un observable calculado.

Pero que de cambios!… en primer lugar es hora de definir nuestro view model de manera tal que se comporte de una forma similar a un objeto de C# (salvando las distancias y limitaciones del lenguaje) y que no sea una simple estructura. En segundo lugar la función ko.computed(function(){…}) nos permite definir una función enlazante que actualiza esta propiedad calculada cuando cambia alguno de los observables de los que su evaluación depende.

Página Web v3.0

Página Web v3.0

Y aún hay más!

Imaginemos ahora que queremos visualizar una lista de bloggeros. Lo primero que debemos hacer es crear un array del modelo de persona y de alguna manera enlazarlo a los elementos del DOM, correcto? Bien avancemos entonces y lo primero que vamos a modificar es la estructura del HMTL:

HTML - Definiendo los elementos y enlaces para visualizar la lista de personas.

HTML – Definiendo los elementos y enlaces para visualizar la lista de personas.

Apliquemos los cambios al archivo JS:

JS - Definiendo un view model para un array de objetos y enlazarlos.

JS – Definiendo un view model para un array de objetos y enlazarlos.

Como podemos observar agregamos un nuevo view model que tiene como atributo una función observableArray (en KO las matrices de objetos se deben definir siempre con dicha función). En las propiedades que son observableArray cada vez que se le agrega o elimina un elemento, estos cambios son notificados a los elementos enlazados (en nuestro caso una lista) y por supuesto irán modificando el DOM.

Resultado:

Página Web v4.0

Página Web v4.0

Para listar los elementos usamos el control de flujo foreach, sin embargo KO también nos provee de otros controles adiciones tales como if, ifnot y with.

Veamos un ejemplo muy simple del control de flujo if y ifnot (el primero evalúa una condición verdadera y el segundo una condición falsa):

HTML - Declarando controles de flujo.

HTML – Declarando controles de flujo.

Resultado:

Página Web v5.0

Página Web v5.0

Por último el control de flujo with es útil en objetos que tienen relaciones con otros objetos (por ejemplo una persona podría tener una objeto dirección y un objeto informaciónContacto relacionados). With nos permite entonces enlazar de una manera muy simple estos elementos:

HTML - Declarando control de flujo with.

HTML – Declarando control de flujo with.

Conclusión: KO nos permite trabajar del lado del front-end de una forma mucho mas organizada, prolija y sencilla, evitándonos mucho trabajo en lo que refiere en enlace de datos con los elementos del DOM. También nos permite tener un comportamiento mucho más fluido en la UI y por ende una experiencia de uso mucho mas agradable (similar a una aplicación Win 8). Seguramente habrá que evaluar el tipo de proyecto antes de implementar KO, pero en general considérelo como una buena alternativa.

Espero que les haya resultado útil!

ASP.NET MVC 4 – Minification and Bundling

Una de las nuevas features de ASP.NET MVC 4 es la de minification and bundling (también denominada web optimization), la cual nos permite compactar y agrupar archivos de Javascript y CSS en nuestra aplicación o sitio web. Esta característica es proporcionada por la librería System.Web.Optimization, que tenemos disponible por default en nuestros proyectos a partir de la versión RC de ASP.NET MVC 4.

Un punto importante, es que a partir de esta feature surge un nuevo concepto, los bundles:

Bundle es simplemente una agrupación lógica de archivos que se pueden referenciar mediante un único nombre, y que se cargan en una única petición HTTP.

La agrupación (bundling) generalmente incluye uno o más archivos de scripts JS o CSS relacionados entre sí. Estos archivos son compactados y optimizados permitiéndonos descargarlos por única vez del servidor ahorrándonos varios request.

La compactación (monification) se realiza en el server y ocurre una única vez, este proceso elimina todos los espacios y saltos de líneas de los archivos incluidos en el paquete (bundle), ademas de renombrar todas las variables por nombres muchos más simples y pequeños. El resultado final: archivos muchos más livianos que ofrecen una mejor performance en el cliente.

Ahora veamos de que se trata esto de Bundles. Si generamos una nueva Web Application ASP.NET MVC 4 podremos ver en ciertas vistas algo similar a lo siguiente:

Utilizando bundles

Utilizando bundles

En este punto, le estamos diciendo a la vista es que utilice bundles, previamente definidos, para los estilos @Style y para los scripts JS @Scripts (veremos más adelante donde definir los paquetes). De esta forma ya no es necesario registrar estos recursos utilizando los tags <scripts> o <styles>.

Si prestan atención al ejemplo, le estamos pasando como parámetros  al método Render() la ubicación y nombre del bundle. Dicha definición del paquete (nombre, path y archivos que lo componen), se realiza en la clase BundleConfig (App_Start\BundelConfig.cs):

BundleConfig

BundleConfig

Los bundles serán generados por única vez cuando se inicie la aplicación. En el método Application_Start() del global.asax encontrarán la inicialización de los mismos:

BundleConfig.RegisterBundles(BundleTable.Bundles);

La definición de un nuevo bundle es bastante simple: creamos un objeto ScriptBundle y le pasamos como parámetro el “path virtual” (ubicación y nombre del paquete), el cual nos permitirá identificarlo desde las vistas. Acto seguido, indicamos que archivos vamos a incluir en el paquete. Existen ciertos atajos para poder especificar los archivos que componen un paquete, podemos utilizar por ejemplo el * para indicar el conjunto de archivos que comienzan con el mismo nombre: jquery-1.*.

Algunos puntos importantes, del mecanismo de bundles. Uno es que tanto los versiones de scripts JS para documentación o previamente minificadas (*.min.js, *-vsdoc.js, *.debug.js) son “descartados” al momento de la compactación. Otro es que el orden en que registramos los archivos dentro de un bundle son respetados al momento de generar el paquete, lo que evita problemas de referencia entre scripts.

Sin embargo, cuando ejecutemos nuestra aplicación nos vamos a encontrar con que ninguna de las características mencionadas anteriormente se hicieron efectivas:

Request realizados al servidor

Request realizados al servidor

Pero a no preocuparse!. Para poder implementar esta feature, simplemente debemos des-habilitar el modo “debug”. Para eso simplemente modificamos la entrada compilation en el archivo web.config:

Modificando el modo de compilación

Modificando el modo de compilación

Esto es así, ya que en el ambiente de desarrollo generalmente necesitamos depurar nuestros archivos de scripts o css, y no tiene sentido el uso de bundles. Sin embargo, si quisiéramos forzar su uso sin afectar el modo de compilación, podemos hacerlo de la siguiente manera (en el método Application_Start() del global.asax):

BundleTable.EnableOptimizations = true;

Ya sea modificando el web.config o habilitando la propiedad BundleTable.EnabledOptimization, si ejecutamos nuevamente la aplicación nos encontraremos con lo siguiente:

Request realizados al servidor

Request realizados al servidor

Como podemos ver, ya no fueron necesarios 15 request para obtener todos los scripts y css. Con solo 4 request trajimos los paquetes compactados y optimizados.

Si revisamos en detalle los archivos que fueron descargados en cada request, podemos ver que tanto el nombre como la ubicación de los paquetes son los que definimos como “path virtual”:

Ubicación y nombres de los bundles

Ubicación y nombres de los bundles

Pero esto no es todo, si volvemos a solicitar la página, vamos a encontrarnos con que los paquetes se encuentran en cache – HTTP Status Code 304 – Not Modified -, por lo que ya no es necesario volver a solicitarlos! 🙂

Cache de Bundles

Cache de Bundles

Inspeccionando un poco más la petición del bundle, se puede observar que a la URL se añade un parámetro “v” con un valor hash. Este es utilizado para identificar cuando un paquete fue modificado, y por lo tanto es necesario volver a buscarlo nuevamente en el servidor.

Como podemos ver, el mecanismo de bundles, no solo se trata de agrupar o compactar archivos. También nos permite manejar de una manera transparente cuestiones como la cache o cambios de versión de los recursos, que a veces son pasadas por alto.

Por último les dejo un par de post excelentes sobre el tema de José M. Aguilar, Scott Hanselman y Rick Anderson.

Espero que les sea de utilidad!