Bueno… Vaya título me ha salido, eh? 😛 A ver, realmente este post es para evitar que alguien pierda el mismo tiempo que he pedido yo, para una chorrada…
En fin, al tajo. No sé si conocéis jQuery templates. Para los que no, que sepáis que es un plugin de jQuery para convertir objetos json en html. No es la única manera de hacerlo, hace tiempo escribí sobre PURE (http://beebole.com/pure/) otra herramienta para hacer lo mismo, y que os animo a que al menos le echéis un vistazo. Poco después apareció la alfa de jquery-tmpl (y siguiendo con el autobombo escribí una pequeña comparativa, que, todo debe reconocerse, hoy ha quedado un poco desfasada). Poco después se anunció que jQuery-tmpl pasaba a ser considerado plugin oficial de jQuery y se pasó a llamar “jQuery templates”. Actualmente está en beta, pero ya es extremadamente estable. Luis Ruiz Pavón escribió un artículo introductorio a jQuery templates que os recomiendo que le echéis un vistazo (aunque la sintaxis actual sea un poco diferente a la de ese artículo, es lo que tiene escribir sobre versiones alfa y demás).
En jQuery templates se usan básicamente “tres» cosas:
- Una definición de template, que se suele incorporar dentro de un tag <script> con un type inválido para que el navegador lo ignore (no lo incorpore al DOM).
- Un contenedor donde se incrustará el DOM generado.
- Un objeto json inicio de los datos.
La sintaxis para definir el template es el punto fuerte de jQuery templates:
<script type="text/javascript">
$(document).ready(function () {
var data = { nombre: 'edu', twitter: 'eiximenis' };
$("#template").tmpl(data).appendTo("#placeholder");
});
</script>
<script id="template" type="text/x-jquery-tmpl">
<div style="background: green">Usuario ${nombre} - Twitter: <a href="http://twitter.com/${twitter}">${twitter}</a>
</script>
<div id="placeholder"></div>
Este código, en tiempo de ejcución genera el DOM siguiente:
<div style="background: green;">Usuario edu - Twitter: <a href="http://twitter.com/eiximenis">eiximenis</a></div>
Hay varios tags para controlar el template. Para este post vamos a ver uno, que es {{each}} que permite repetir parte del template por cada elemento del array:
<script type="text/javascript">
$(document).ready(function () {
var data = {
titulo: 'Twitters en geeks',
users: [{ nombre: 'edu', twitter: 'eiximenis' },
{ nombre: 'jorge', twitter: 'j0rgeSerran0' },
{ nombre: 'javi', twitter: 'jtorrecilla' },
]
};
$("#template").tmpl(data).appendTo("#placeholder");
});
</script>
<script id="template" type="text/x-jquery-tmpl">
<div style="background: #EEEEEE">
<h4>${titulo}</h4>
{{each users}}
Usuario ${$value.nombre} - Twitter: <a href="http://twitter.com/${twitter}">${$value.twitter}</a><br />
{{/each}}
</div>
</script>
<div id="placeholder"></div>
Con {{each}} iteramos sobre los elementos del array “users”. Dentro del template el valor $value me permite referenciar el valor del array para el que se está renderizando el template (p.ej. ${$value.nombre} me permite acceder al nombre del elemento que se está renderizando). Si el nombre de $value no nos gusta, lo podemos indicar como parámetro de each:
<script id="template" type="text/x-jquery-tmpl">
<div style="background: #EEEEEE">
<h4>${titulo}</h4>
{{each(idx, user) users}}
<strong>${idx}:</strong>Usuario ${user.nombre} - Twitter: <a href="http://twitter.com/${twitter}">${user.twitter}</a><br />
{{/each}}
</div>
</script>
En este código podemos usar ${idx} para acceder al índice del elemento y ${user} para acceder a cada elemento que se está renderizando.
Bien, vayamos ahora al tema del post…
Una cosilla interesante es que a la llamada a tmpl() se le puede pasar un segundo parámetro, con datos adicionales globables que pueden usarse en el template. Esos parámetros pueden ser, entre otras cosas, funciones. Para acceder a los elementos “globales” se usa la variable de template $item. Imaginad que tenemos esto ahora:
<script type="text/javascript">
$(document).ready(function () {
var data = {
titulo: 'Twitters en geeks',
users: [{ nombre: 'edu', twitter: 'eiximenis' },
{ nombre: 'jorge', twitter: 'j0rgeSerran0' },
{ nombre: 'javi', twitter: 'jtorrecilla' },
{ nombre: 'alguien' }
]
};
$("#template").tmpl(data,
{ getUrl: function (name) { return name != undefined ? "http://twitter.com/" + name : '#'; } }).appendTo("#placeholder");
});
</script>
Como segundo parámetro a tmpl() le pasamos un objeto con una función getUrl que dado una cadena me construirá la URL de twitter asociada. Si el la cadena es undefined, me generará una URL que no haga nada (#). Ahora toca llamar a esta función desde el template:
<script id="template" type="text/x-jquery-tmpl">
<div style="background: #EEEEEE">
<h4>${titulo}</h4>
{{each(idx, user) users}}
<strong>${idx}:</strong>Usuario ${user.nombre} -
Twitter: <a href="${$item.getUrl(${user.twitter})}">${user.twitter}</a><br />
{{/each}}
</div>
</script>
Fijaos que llamamos a getUrl usando $item.getUrl() (porque getUrl está definido en el ámbito global del template). Y como parámetro le pasamos el valor de la propiedad twitter del elemento que estamos renderizando. Esto, yo suponía que era ${user.twitter}. Pero eso no funciona. No aparece el template y en su lugar aparece un error de javascript en jquery-tmpl.js. Eso es muy común: errores en el template generan errores de javascript dentro de jquery-tmpl.js.
Después de varios intentos descubrí que pasaba: Cuando se pasan parámetros a una función definida dentro del ámbito global del template esos parámetros de pasan sin ${}. Es decir debemos usar:
<script id="template" type="text/x-jquery-tmpl">
<div style="background: #EEEEEE">
<h4>${titulo}</h4>
{{each(idx, user) users}}
<strong>${idx}:</strong>Usuario ${user.nombre} -
Twitter: <a href="${$item.getUrl(user.twitter)}">${user.twitter}</a><br />
{{/each}}
</div>
</script>
Fijaos en la llamada a getUrl, ahora simplemente la pasamos getUrl(user.twitter). Y eso funciona correctamente!
Así pues:
- ${$item.getUrl(${user.name})} –> NO FUNCIONA
- ${item.getUrl(user.name)} –> FUNCIONA CORRECTAMENTE
En fin… cosillas que descubre uno 😉
Saludos!
Hola Eduard,
Tu post me ha dado la idea de escribir una página con un ejemplo de como usar(y no pasar) funciones o datos al código de template de pure.js
http://beebole.com/pure/documentation/using-closures/
No te parece un poco acrobatico lo que tienes que hacer para tener tu función getUrl en tu template? Y que pasa si esa función usa otra, o es metodo de un objeto?
Buenas Mic!
Perdona por el retraso, pero se me pasó tu comentario… 🙁
La verdad, no he probado ninguno de los casos que mencionas en jquery templates, pero la verdad es que el uso de funciones externas tiene sus limitaciones.
PURE tiene otra filosofía y he de decir que cuando lo usé me gustó mucho 😉
Saludos!