Estas plantillas vienen muy bien para empezar, pero luego nos surgen necesidades naturales, como por ejemplo ¿Cómo resaltar un elemento dentro de una lista? ¿Cómo representar elementos con información diferente dentro de una misma colección?
Si nos fijamos en algunas aplicaciones que ya tenemos en Windows 8, dentro de una colección podemos tener diferentes representaciones de los elementos, dependiendo de la importancia, tipo de elemento, etc. Por ejemplo, en la tienda de aplicaciones Windows vemos iconos más grandes para los elementos destacados:

Conseguir este efecto puede parecer muy complicado desde la plantilla, pues todos los elementos utilizan la misma. Por suerte, la librería WinJS nos proporciona las herramientas necesarias para poder controlar la representación de nuestros elementos mediante diferentes mecanismos:

var sampleItems = [
{ group: sampleGroups[0], importancia:"alta", title: "Item Title: 1", su
{ group: sampleGroups[0], importancia:"baja", title: "Item Title: 2", su
{ group: sampleGroups[0], importancia: "baja", title: "Item Title: 3", s
{ group: sampleGroups[0], importancia: "media", title: "Item Title: 4",
{ group: sampleGroups[0], title: "Item Title: 5", subtitle: "Item Subtit
{ group: sampleGroups[1], importancia: "baja", title: "Item Title: 1", s
{ group: sampleGroups[1], importancia: "baja", title: "Item Title: 2", s
{ group: sampleGroups[1], importancia: "alta", title: "Item Title: 3", s
{ group: sampleGroups[2], importancia: "alta", title: "Item Title: 1", s
{ group: sampleGroups[2], title: "Item Title: 2", subtitle: "Item Subtit
{ group: sampleGroups[2], title: "Item Title: 3", subtitle: "Item Subtit
{ group: sampleGroups[2], title: "Item Title: 4", subtitle: "Item Subtit
Como ahora tenemos un campo por el que realizar una selección, podemos ir al código HTML y cambiar la plantilla. Abrimos la página groupedItems.html y buscamos el elemento <div> que tiene la clase itemtemplate. En este elemento vamos a añadir un enlace de datos al elemento item-title que quedará de la siguiente manera:
<h4 class="item-title" data-win-bind="textContent: title; className: importancia"></h4>
La propiedad className se corresponde en JavaScript al atributo class de HTML. La hemos enlazado al campo importancia que hemos creado en algunos elementos de la lista anterior. De esta manera, dependiendo del elemento, la clase se redefinirá. Ahora sólo nos queda generar tantas clases CSS como necesitemos. En el archivo groupedItems.css añadimos estas tres clases:
.alta {
margin: -6px -15px -2px -15px;
padding: 6px 15px 2px 15px;
-ms-grid-row: 1;
overflow: hidden;
width: 250px;
background-color: red;
}
.media {
margin: -6px -15px -2px -15px;
padding: 6px 15px 2px 15px;
-ms-grid-row: 1;
overflow: hidden;
width: 250px;
background-color: blue;
}
.baja {
margin: -6px -15px -2px -15px;
padding: 6px 15px 2px 15px;
-ms-grid-row: 1;
overflow: hidden;
width: 250px;
background-color: green;
}
Y así conseguiremos un aspecto diferente según la importancia del elemento:

En el ejemplo, vamos al script groupedItems.js y buscamos la función ready. Encontraremos la línea donde se asigna el itemTemplate al elemento listView, por ahora se le asigna el elemento a partir de la plantilla que tenemos en el HTML. Sustituimos esa línea por el siguiente código:
listView.itemTemplate = function (itemPromise) {
return itemPromise.then(function (item) {
var div = document.createElement("div");
div.style.height = "250px";
div.style.width = "250px";
var childDiv = document.createElement("div");
var title;
switch (item.data.importancia) {
case "alta":
div.style.height = "510px";
childDiv.style.padding = "10px";
title = document.createElement("h2");
div.style.backgroundColor = "red";
break;
case "media":
div.style.width = "510px";
childDiv.className = "item-overlay";
title = document.createElement("h3");
div.style.backgroundColor = "blue";
break;
case "baja":
childDiv.className = "item-overlay";
title = document.createElement("h4");
div.style.backgroundColor = "green";
break;
default:
div.style.msGridRows = "1fr 90px";
div.style.backgroundColor = "rgb(250,150,0)";
title = document.createElement("h5");
childDiv.style.backgroundColor = "rgba(0,0,0,0.5)";
childDiv.style.msGridRow = "2";
childDiv.style.padding = "10px";
var image = new Image();
image.src = "/images/logo.png";
image.style.width = "100%";
image.style.height = "100%";
image.style.msGridRowSpan = "2";
div.appendChild(image);
break;
}
title.className = "item-title";
title.innerText = item.data.title;
childDiv.appendChild(title);
div.appendChild(childDiv);
return div;
});
};
Vemos como la función asignada a la plantilla del elemento recibe un objeto Promise. Esto quiere decir que se ejecuta de forma asíncrona. Esta función recibirá el elemento que se está representado, en el campo data tendremos el ítem que se debe representar. Dentro de la función hemos creado un selector por el campo importancia; dependiendo de la importancia del ítem estamos generando elementos diferentes, con diferentes tamaños y contenido. Los diferentes tamaños se ven representados en la lista, pero no como nos esperamos que se vean:

Para que el elemento ListView sepa mejor como apiñar los elementos, tenemos que proporcionarle algo más de información. Debemos activar la capacidad de expandir los elementos entre celdas y proporcionarle el tamaño del elemento mínimo; el resto de elementos deberían ser un múltiplo de este. Para que el rendimiento sea aceptable, debemos buscar el elemento más grande que cumpla con los requisitos, pues si ponemos 1x1 funcionará, pero penalizará el rendimiento una barbaridad. La fórmula es la siguiente:
Así, para nuestro caso, el tamaño mínimo es 250x250 (añadiendo el margen, el siguiente tamaño sería 510x250 o 250x510). Localizamos en la función initializeLayout la segunda inicialización del listview.layout, donde se asigna el GridLayout y lo modificamos como sigue:
listView.layout = new ui.GridLayout({
groupHeaderPosition: "top",
groupInfo: function () {
return {
enableCellSpanning: true,
cellWidth: 250,
cellHeight: 250
};
}
});
Ahora al ejecutar la aplicación ya podemos ver los elementos bien colocados.

Para conseguir el mismo efecto con plantillas y evitar a las hordas de diseñadores enfandados, vamos a crear una plantilla para cada estado en la página groupedItems.html:
<div class="verybigitemtemplate" data-win-control="WinJS.Binding.Template">
<div class="verybigitem">
<div class="iteminfo">
<h2 class="itemtitle" data-win-bind="textContent: title"></h2>
</div>
</div>
</div>
<div class="bigitemtemplate" data-win-control="WinJS.Binding.Template">
<div class="bigitem">
<div class="iteminfo">
<h3 class="itemtitle" data-win-bind="textContent: title"></h3>
</div>
</div>
</div>
<div class="itemtemplate" data-win-control="WinJS.Binding.Template">
<div class="item">
<div class="iteminfo">
<h4 class="itemtitle" data-win-bind="textContent: title"></h4>
</div>
</div>
</div>
<div class="undefineditemtemplate" data-win-control="WinJS.Binding.Template">
<div class="undefItem">
<img class="itemimage" src="/images/logo.png" />
<div class="iteminfo">
<h5 class="itemtitle" data-win-bind="textContent: title"></h5>
</div>
</div>
</div>
Ahora que tenemos una plantilla para cada estado, necesitamos definir por CSS el estilo de cada una. Como hemos creado una clase para cada elemento, podremos en el groupedItems.css definir tamaños, colores, etc, para cada tipo de elemento:
.verybigitem {
height:510px;
width:250px;
background-color:red;
}
.bigitem {
height:250px;
width:510px;
background-color:blue;
}
.item {
height: 250px;
width: 250px;
background-color: green;
}
.undefItem {
display: -ms-grid;
-ms-grid-rows: 1fr 90px;
height: 250px;
width: 250px;
background-color: rgb(250,150,0);
}
Para que estas definiciones de CSS tengan efecto, quitamos la definición para la clase .win-item, podemos eliminarla o simplemente comentarla, fijaos que la definición es muy parecida a la que tenemos para el .undefItem:
/*.groupeditemspage .groupeditemslist .win-item {
-ms-grid-columns: 1fr;
-ms-grid-rows: 1fr 90px;
display: -ms-grid;
height: 250px;
width: 250px;
}*/
Ahora necesitamos un poco de código en la asignación de la plantilla, pero tranquilos, nuestros diseñadores ni se enterarán:
listView.itemTemplate = function (itemPromise) {
var bigtemplate = element.querySelector(".bigitemtemplate").winControl;
var verybigtemplate = element.querySelector(".verybigitemtemplate").winControl;
var smalltemplate = element.querySelector(".itemtemplate").winControl;
var undefinedtemplate = element.querySelector(".undefineditemtemplate").winControl;
return itemPromise.then(function (currentItem) {
var item = currentItem.data; // aquí tenemos el elemento.
var itemTemplate;
switch(item.importancia){
case "alta":
itemTemplate = verybigtemplate;
break;
case "media":
itemTemplate = bigtemplate;
break;
case "baja":
itemTemplate = smalltemplate;
break;
default:
itemTemplate = undefinedtemplate;
break;
}
return itemTemplate.render(item);
});
};
El truco está en recuperar los elementos de tipo plantilla y luego llamar a la función "render" del mismo. Así será la propia plantilla que generará el HTML junto con los enlaces de datos que lleve hacia el elemento que le estamos pasando.
Finalmente, por si no lo habíamos hecho en el ejercicio anterior, comprobamos que el GridLayout está configurado para admitir tamaños diferentes de elemento:
listView.layout = new ui.GridLayout({
groupHeaderPosition: "top",
groupInfo: function () {
return {
enableCellSpanning: true,
cellWidth: 250,
cellHeight: 250
};
}
});
Y aquí tenemos el resultado. Le falta un poco de diseño (márgenes, colores adecuados, etc...) pero eso ya se lo dejaremos al diseñador a cambio de haberle liberado de escribir JavaScript.
