Vamos a ver cómo tenemos que gestionar los datos para poder cargar dicha vista de información, al tiempo que refrescaremos los puntos en que dicha conexión se produce.
Este artículo muestra los cambios más recientes realizados como parte del lanzamiento de Windows Release Review y Visual Studio 2012.
La clase Data
En varios proyectos de aplicación Metro que vienen por defecto con el último Visual Studio vamos a encontrar un fichero JavaScript denominado data.js. Este fichero contiene la información de "relleno" que las aplicaciones muestran por defecto, a la espera de que las modifiquemos para darles verdadera funcionalidad.Este fichero tiene una estructura sencilla de seguir. En primer lugar, aparece una serie de valores por defecto para descripciones e imágenes, seguido por la definición de ítems y grupos de ítems, cada uno con una serie de propiedades que va a estar conectada a la vista. En el siguiente fragmento podemos ver ejemplos de las propiedades de un grupo y de un ítem.
// Grupo
{
key: "group1",
title: "Group Title: 1",
subtitle: "Group Subtitle: 1",
backgroundImage: darkGray,
description: groupDescription
}
// Item
{
group: sampleGroups[0],
title: "Item Title: 1",
subtitle: "Item Subtitle: 1",
description: itemDescription,
content: itemContent,
backgroundImage: lightGray
}
Una vez definida la información y su estructura, aparece una serie de métodos que van a servir para localizar información dentro de los grupos e ítems:
- getItemReference genera una "referencia" a un ítem, a partir de su grupo y de su título; resolveItemReference es capaz de leer esa estructura y encontrar el ítem original.
- resolveGroupReference: encuentra un grupo a partir de su clave.
- getItemsFromGroup: devuelve todos los ítems de un grupo.
Esto se conseguirá haciendo uso de WinJS.Binding.List, tal como se ve en el fichero. Mediante el método createGrouped, la lista recibirá dos funciones:
- La primera, ejecutada para cada elemento de la lista, devuelve la clave del grupo de cada ítem.
- La segunda, ejecutada en un único elemento de cada grupo, devuelve la información del grupo en sí.
- Es posible pasar una tercera función que sirve para elegir el orden de los elementos dentro de los grupos; es útil si queremos una ordenación basada en alguno de los atributos de los ítems, como su fecha o su nombre.
var list = new WinJS.Binding.List();
var groupedItems = list.createGrouped(
function groupKeySelector(item) { return item.group.key; },
function groupDataSelector(item) { return item.group; }
);
La variable groupedItems es una suerte de "proyección" sobre la lista original, de forma que al introducir nuevos elementos en la lista, groupedItems va a actualizarse también de forma automática. Eso es, precisamente, lo que hace la iteración sobre sampleItems, que va a introducir en la lista todos los ítems, de forma que groupedItems se cargará con ellos agrupados con base en el grupo de cada uno de ellos.
El último fragmento del fichero es muy interesante: define un nuevo espacio de nombres denominado "Data", con varios elementos.
WinJS.Namespace.define("Data", {
items: groupedItems,
groups: groupedItems.groups,
getItemsFromGroup: getItemsFromGroup,
getItemReference: getItemReference,
resolveGroupReference: resolveGroupReference,
resolveItemReference: resolveItemReference
});
Una de las buenas prácticas que deberíamos utilizar siempre al desarrollar aplicaciones Metro con JavaScript (y, en general, cuando programemos con cualquier lenguaje) es reducir la visibilidad de la información a lo mínimo imprescindible. De este modo, entre otros muchos beneficios, conseguiremos evitar interacciones inesperadas entre partes del código.
Una de las formas de implementar esta ocultación de la información en JavaScript es el uso de funciones anónimas. Si nos fijamos atentamente, nos daremos cuenta que todos los ficheros JavaScript de las plantillas de proyecto de aplicación Metro cumplen la siguiente estructura:
(function () {
Bibliografía
"use strict";
Bibliografía
// Eliminado por claridad...
})();
Con ello conseguimos que todos los elementos definidos en el interior del método no sean visibles desde fuera de la función, evitando también que sean manipulados. Esta clara ventaja tiene un inconveniente: en ocasiones necesitamos definir elementos para que sean visibles desde fuera de la función anónima.
Ahí entra en juego el método WinJS.Namespace.define. Nos permite definir un nuevo espacio de nombres en el que colocar elementos que sí van a ser visibles desde fuera de la función anónima. En el caso del código que veíamos antes se trata de datos y funciones que van a ser llamados por la vista o por otro código JavaScript de la aplicación.
Volviendo a la vista...
Si volvemos un momento al código HTML de la página groupedItems.html, recordaremos que había dos templates de datos definidas. Estas templates iban a servir para darle formato al objeto WinJS.UI.ListView que, en realidad, representaba la página. Si tomamos la template para los ítems de ListView, veremos algunos nombres familiares:
<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
<div class="item-overlay">
<h4 class="item-title" data-win-bind="textContent: title"></h4>
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
</div>
</div>
En la etiqueta <img> estamos usando la propiedad data-win-bind para enlazar los atributos src y alt de la etiqueta con, precisamente, las propiedades backgroundImage y title que ya habíamos visto definidas en Data.sql, en concreto en sus ítems. Lo mismo aplica para los atributos de las etiquetas <h4> y <h6>, enlazado su contenido a las propiedades title y subtitle de cada ítem. Como vemos, declarativamente en HTML estamos identificando qué propiedades de la fuente de información subyacente de la lista queremos mostrar.
... y terminando con el código controlador en JavaScript
Y el círculo se cierra con este código del fichero groupedItems.js:
// This function updates the ListView with new layouts
initializeLayout: function (listView, viewState) {
/// <param name="listView" value="WinJS.UI.ListView.prototype" />
if (viewState === appViewState.snapped) {
listView.itemDataSource = Data.groups.dataSource;
listView.groupDataSource = null;
listView.layout = new ui.ListLayout();
} else {
listView.itemDataSource = Data.items.dataSource;
listView.groupDataSource = Data.groups.dataSource;
listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
}
}
Dependiendo del estado de la vista, cargaremos en la lista como fuente de información Data.groups, mostrando sólo los grupos para crear una vista más simplificada; o Data.items para mostrar toda la información, organizándola mediante Data.groups.
Conclusiones
Con este artículo cerramos la parte inicial dedicada a las templates y al binding de datos. Hemos visto cómo, en una especie de patrón Modelo-Vista-Controlador, el fichero Data.js ejercía el papel de modelo, las páginas HTML representan la vista y dan formato a la información mediante templates, y el código JavaScript de esas páginas realiza la función de controlador, conectando el modelo adecuado con la vista correcta.
Bibliografía
Centro de Desarrollo de Windows (español)Javier Holguera
Desarrollador senior con tecnología .NET en Payvision.