Vista Template engine
El sistema de plantillas por defecto de play está basado en Groovy. La versión de play de Scala, viene con un sistema de plantilla basado en Scala. La ventaja de este sistema de plantilla es que Scala es un lenguaje de tipado estático, por lo tanto si hay error de tipo los dará en tiempo de compilación y no en tiempo de compilación. Para la versión 2.0 de Play tienen planeado sustituir el motor de plantillas de Groovy por el de Scala.
Los elementos básicos del sistema de plantilla son:
- Expresiones ${}
- Decoradores #{extends /} y #{doLayout /}
- Tags #{tagName /}
- Acciones @{…} (Relativas) or @@{…} (Absolutas)
- Mensajes &{…}
- Comentarios *{…}*
- Scripts %{…}%
Nuestra primera plantilla
Ya teníamos creado nuestro primero controlador, ahora nos hace falta la primera plantilla para poder ver los resultados. Lo que haremos es listar la lista de tweets.
Creamos el fichero con apps/views/Timeline/index.html
${tweets}
1
2
3
4
#{extends ‘main.html’ /}
{set title:‘Timeline’ /}
Con el extends estamos diciendo que plantilla queremos utilizar. El set nos permite fijar variables que se van a utilizar dentro de la plantilla. Con ${tweets} estamos mostrando los tweets, que fueron pasados como parámetros del render.
Abrimos el navegados http://localhost:9000/
Y deberíamos ver algo tipo:
[]
Esta es la representación que hace Groovy de una lista vacía. Nuestra lista de tweets está vacia, así que vamos a llenarla con algunos datos de pruebas.
Poblando la base de datos al arranque de la aplicación
Como vamos a utilizar una base de datos en memoria, nos interesa que cada vez que arranque la aplicación se llene con algunos datos de prueba. Para ellos vamos a utilizar los Jobs de Play.
Creamos una nueva clase en app/app/Bootstrap.java
import models.User;
import play.jobs.Job;
import play.jobs.OnApplicationStart;
import play.test.Fixtures; @OnApplicationStart
public class Bootstrap extends Job { }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package app;
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">doJob</span><span class="o">(){</span>
<span class="k">if</span><span class="o">(</span><span class="n">User</span><span class="o">.</span><span class="na">count</span><span class="o">()</span> <span class="o">==</span> <span class="mi">0</span><span class="o">){</span>
<span class="n">Fixtures</span><span class="o">.</span><span class="na">loadModels</span><span class="o">(</span><span class="s">"initial-data.yml"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
Con la anotación @OnApplicationStart le estamos diciendo a Play que queremos que nuestro Job se ejecute cada vez que arranque la aplicación. Los Job deben extender de la clase play.jobs.Job e implementar el método doJob que es el método que se invocará. En el método estamos utilizando la clase Fixtures que ya conocemos para cargar datos en base de datos a partir de un fichero yml.
Además de la anotación @OnApplicationStart existen otras anotaciones para programar Jobs:
- @Every - Ejecuta tareas en intervalos de tiempo
- @On - Permite utilizar una sintaxis tipo CRON
Si actualizamos la página http://localhost:9000/ deberíamos ver algo parecido a:
[Tweet[9], Tweet[8], Tweet[7], Tweet[6], Tweet[5], Tweet[4], Tweet[3], Tweet[2], Tweet[1]]
Utilizando scripts dentro de las vistas
Hasta ahora lo que estábamos mostrando es el .toString() de los objetos, que por defecto muestra NombreClase[id]. Vamos a cambiar nuestra vista para que muestra la información de cada uno de los tweets.
Dentro de los bloques de %{}% podemos utilizar código groovy. Por ejemplo, un bucle for para recorrer todos los tweets.
%{
for(tweet in tweets){
}%
<div class=“tweet”>
<div class=“tweet-author”>${tweet.author.username}</div>
<div class=“tweet-msg”>${tweet.msg}</div>
<div class=“tweet-date”>${tweet.date}</div>
</div>
%{
}
%}
1
2
3
4
5
6
7
8
9
10
11
12
13
#{extends ‘main.html’ /}
{set title:‘Timeline’ /}
Utilizando tags
Los bloques de scripts están bien, pero a veces puede quedar la sintaxis un poco engorrosa. Para solucionar esto están los #{tags /}. Play viene con una serie de tags por defecto.
En este caso nos vendría bien utilizar el tag #{list /}
1
2
3
4
5
6
7
8
9
10
#{extends ‘main.html’ /}
{set title:‘Timeline’ /}
{list items:tweets, as:‘tweet’}
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tweet"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tweet-author"</span><span class="nt">></span>${tweet.author.username}<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tweet-msg"</span><span class="nt">></span>${tweet.msg}<span class="nt"></div></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"tweet-date"</span><span class="nt">></span>${tweet.date}<span class="nt"></div></span>
<span class="nt"></div></span>
{/list}
Creando nuestros propios tags
Puedes definirte tus propios tags y escribirlos en Groovy o directamente en Java. Es una buena práctica crear tags para elementos que repitas en varias páginas, así el código no se duplica, sino se sustituye por llamadas al tag.
En este caso, parece que la forma de representar un tweet puede repetirse en varias páginas, por ejemplo el timeline, la lista de mensajes privados y así. Vamos a crear un tag para no tener que repetir el código.
Los tags se crean en la carpeta app/view/tags. El nombre del fichero, será el nombre del tag. Los tags pueden recibir parámetro. Dentro del tag estarán disponibles con $nombreParametro. El parámetro por defecto es $arg.
app/views/tags/renderTweet.html
1
2
3
4
5
<div class=“tweet”>
<div class=“tweet-author”>${arg.author.username}</div>
<div class=“tweet-msg”>${arg.msg}</div>
<div class=“tweet-date”>${_arg.date}</div>
</div>
Cambiamos nuestra vista para hacer la llamada al tag
app/view/Timeline/index.html
1
2
3
4
5
6
#{extends ‘main.html’ /}
{set title:‘Timeline’ /}
{list items:tweets, as:‘tweet’}
#{renderTweet tweet /}
{/list}
Ejercicio: Tag para mostrar gráfica
Como ejercicio vamos a crear un tag que permita mostrar una graáfica.
Pasos:
- Crear una nueva página de estadísticas en /stats
- Consultar cuantos tweets ha escrito cada uno de los usuarios
- Utilizando Google Chart http://code.google.com/intl/es-ES/apis/chart/interactive/docs/gallery/columnchart.html crear una gráfica que muestre en el eje de las X los usuarios y en el eje de las Y el número de tweets que ha publicado.
- Crear un tag que permite reutilizar el elemento de las gráficas. La llamada al tags debe ser algo de la forma:
1
2
3
4
5
6
7
8
#{grafica items:user_tweets,
title:‘Tweets de usuarios’,
xTitle:‘Usuario’,
yTitle:‘Tweets’,
id:‘user_tweet’,
width:500,
height:400
/}
Consejos:
- Utiliza group by y count para contar cuantos tweets ha escrito cada usuario Documentación
- El tag lista permite obtener el indice del elemento por el que va el bucle en la variable item_index
Solución
Crea la nueva ruta
conf/routes
1
GET /stats Timeline.stats
Crea el nuevo método del controlador
app/controllers/Timeline.java
1
2
3
4
public static void stats(){
List<Object[]> user_tweets = JPA.em().createQuery(“select t.author.username, count(t) from Tweet t group by t.author”).getResultList();
render(user_tweets);
}
Crea el tag, utilizando la documentación de Google Chart y haciendo la sustitución de variables en los lugares adecuados.
apps/views/tags/grafica.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<script src=“https://www.google.com/jsapi”></script>
<script>
google.load(“visualization”, “1”, {packages:[“corechart”]});
google.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
data.addColumn(‘string’, ‘${xTitle}’);
data.addColumn(‘number’, ‘${yTitle}’);
<span class="nx">data</span><span class="p">.</span><span class="nx">addRows</span><span class="p">(</span><span class="nx">$</span><span class="p">{</span><span class="nx">_items</span><span class="p">.</span><span class="nx">size</span><span class="p">()});</span>
<span class="err">#</span><span class="p">{</span><span class="nx">list</span> <span class="nx">items</span><span class="err">:</span><span class="nx">_items</span><span class="p">,</span> <span class="nx">as</span><span class="err">:</span><span class="s1">'item'</span><span class="p">}</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="nx">$</span><span class="p">{</span><span class="nx">item_index</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> <span class="mi">0</span><span class="p">,</span> <span class="s1">'${item[0]}'</span><span class="p">);</span>
<span class="nx">data</span><span class="p">.</span><span class="nx">setValue</span><span class="p">(</span><span class="nx">$</span><span class="p">{</span><span class="nx">item_index</span> <span class="o">-</span><span class="mi">1</span><span class="p">},</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">$</span><span class="p">{</span><span class="nx">item</span><span class="p">[</span><span class="mi">1</span><span class="p">]});</span>
<span class="err">#</span><span class="p">{</span><span class="sr">/list</span><span class="err">}
var chart = new google.visualization.ColumnChart(document.getElementById(‘${id}’));
chart.draw(data, {width: ${width}, height: ${height}, title: ‘${title}’});
}
</script>
<div id=“${_id}”></div>
Crea la vista donde se llame al tag
apps/views/Timeline/stats.js
1
2
3
4
5
6
7
8
9
10
11
#{extends ‘main.html’ /}
#{set title:‘Estadísticas’ /}
#{grafica items:user_tweets,
title:'Tweets de usuarios',
xTitle:'Usuario',
yTitle:'Tweets',
id:'user_tweet',
width:500,
height:400
/}<span class="w">