Advertisement
  1. Code
  2. Web Development

Una introducción a Handlebars

Scroll to top
Read Time: 13 min

() translation by (you can also view the original English article)

Si los datos de tu sitio cambian con regularidad, es posible que quieras echar un vistazo a Handlebars. Handlebars es un procesador de plantillas que genera dinámicamente tu página HTML, lo que te ahorra tiempo al realizar actualizaciones manuales. En este tutorial, te presentaré Handlebars y te enseñaré cómo crear una plantilla básica para tu sitio.


Plantilla del sitio

Hay dos razones principales por las que querrías crear una plantilla para tu sitio. En primer lugar, la creación de una plantilla te alienta a separar el código basado en la lógica de la vista real, lo que te ayuda a adherirte a un patrón de Vista/Controlador. En segundo lugar, las plantillas mantienen tu código limpio y fácil de mantener, lo que, a su vez, hace que el proceso de actualización de tu sitio sea muy sencillo. No creas un sitio con Handlebars. En su lugar, creas pautas y estructuras que determinan cómo debe verse el sitio sin centrarse en los datos de una página. Cubramos algunos de los conceptos básicos.


Los básicos

Handlebars genera tu HTML tomando una estructura JSON y ejecutándola a través de una plantilla. Estas plantillas se escriben principalmente en HTML normal y están distribuidas con marcadores de posición que te permiten insertar datos, según sea necesario. Por ejemplo, la siguiente plantilla saluda al usuario cuando inicia sesión:

1
<h1>Welcome back, {{name}}</h1>

El atributo {{name}} es donde se inyectará el nombre del usuario en la página. Este marcador de posición corresponde a una propiedad de la estructura JSON de los datos. Este es el ejemplo más básico posible, pero pronto verás que todo lo demás básicamente se reduce a este simple concepto. Pasemos al manejo de las matrices.

Matrices

Handlebars viene con algunos ayudantes incorporados para ayudarte a trabajar con datos más complejos. Uno de estos ayudantes es el ayudante each. Este ayudante itera a través de una matriz y te permite crear HTML dinámico, por elemento de la matriz. Por ejemplo, la siguiente plantilla muestra los datos de una matriz que contiene una lista de los conciertos locales que se reproducen en mi área:

1
<table>
2
	<tr>
3
		<th>Local Concerts</th>
4
	</tr>
5
	{{#each Concerts}}
6
		<tr>
7
			<td>{{this}}</td>
8
		</tr>
9
	{{/each}}
10
</table>

Como puedes ver, este código es mucho más limpio que el código convencional, como usar un bucle en PHP o JavaScript para agregar HTML a una variable. Handlebars no es intrusivo, y esto es lo que hace que Handlebars sea tan accesible. También puedes notar que usamos el nombre del atributo, this, para recuperar el elemento de matriz actual en el bucle each.

Este ejemplo es bueno para una matriz de valores simples, pero ¿cómo manejas datos más complejos? Bueno, esencialmente haces lo mismo. Por ejemplo, escribiremos una plantilla para los siguientes datos:

1
[	
2
	{
3
		Name : "Band",
4
		Date : "Aug 14th, 2012",
5
		Albums : [
6
			{
7
				Name : "Generic Name"
8
			},
9
			{
10
				Name : "Something Else!!"
11
			}
12
		]
13
	},
14
	{
15
		Name : "Other Guys",
16
		Date : "Aug 22nd, 2012"
17
		Albums : [
18
			{
19
				Name : "Album One"
20
			}
21
		]
22
	}
23
]

Podemos mostrar fácilmente esta información usando la siguiente plantilla:

1
<table>
2
	<tr>
3
		<th>Band Name</th>
4
		<th>Date</th>
5
		<th>Album Name</th>
6
	</tr>
7
	{{#each Bands}}
8
		<tr>
9
			<td>{{Name}}</td>
10
			<td>{{Date}}</td>
11
			<td>{{Albums.0.Name}}</td>
12
		</tr>
13
	{{/each}}
14
</table>

Puedes almacenar tu plantilla en un elemento <script /> y cargarlo con JavaScript.

En Handlebars, incluso puedes acceder a propiedades anidadas, como en el ejemplo anterior (Albums.0.Name) y, por supuesto, podría haber usado otro bucle each para iterar sobre los álbumes de una banda. Vale la pena señalar que además de la notación de puntos para acceder a las propiedades anidadas, también puedes utilizar "../" para acceder a las propiedades del padre.

¿Y si no hay bandas tocando? Ciertamente, no quieres una mesa vacía, y Handlebars afortunadamente proporciona los ayudantes if, else y unless. Las declaraciones if y else funcionan como la mayoría de los lenguajes de programación: si el objeto que pasas es false o falsey, entonces se ejecuta la instrucción else. De lo contrario, se ejecuta la instrucción if. La declaración unless es bastante interesante; es esencialmente una declaración if invertida. Si la expresión es true, el bloque unless NO se ejecutará. Así que incorporemos estos ayudantes a nuestro código:

1
{{#if Bands}}
2
	<table>
3
		<tr>
4
			<th>Band Name</th>
5
			<th>Date</th>
6
			<th>Album Name</th>
7
		</tr>
8
		{{#each Bands}}
9
			<tr>
10
				<td>{{Name}}</td>
11
				<td>{{Date}}</td>
12
				<td>{{Albums.0.Name}}</td>
13
			</tr>
14
		{{/each}}
15
	</table>
16
{{else}}
17
	<h3>There are no concerts coming up.</h3>
18
{{/if}}

Ayudantes personalizados

Handlebars te da la posibilidad de crear tu propio ayudante personalizado. Simplemente registra tu función en Handlebars y cualquier plantilla que compiles posteriormente podrás acceder a tu ayudante. Hay dos tipos de ayudantes que puedes crear:

  • Los ayudantes de funciones son básicamente funciones regulares que, una vez registradas, se pueden llamar en cualquier parte de tu plantilla. Handlebars escribe el valor de retorno de la función en la plantilla.
  • Los ayudantes de bloque son de naturaleza similar a los ayudantes if, each, entre otros. Te permiten cambiar el contexto de lo que hay dentro.

Permíteme mostrarte un ejemplo rápido de cada uno. Primero, registraré una función auxiliar con el siguiente código:

1
Handlebars.registerHelper("Max", function(A, B){
2
	return (A > B) ? A : B;
3
});

El primer argumento que se pasa a registerHelper() es el nombre de mi cliente ayudante; usaré este nombre en la plantilla. El segundo argumento es la función asociada con este ayudante.

Usar este ayudante en una plantilla es extremadamente simple:

1
{{Max 12 45}}

Esta plantilla usa el ayudante Max y pasa los valores 12 y 45 a la función asociada. Los ayudantes de funciones de Handlebars admiten varios parámetros. Puedes insertar números directamente en la propia plantilla o puedes utilizar atributos de una estructura JSON.

Ahora veamos un ayudante de bloque personalizado. Los ayudantes de bloque te permiten configurar el contexto antes de ejecutar el código contenido dentro del bloque. Por ejemplo, ten en cuenta el siguiente objeto:

1
{
2
	Name: "Parent",
3
	Sub: {
4
		Name: "Child"
5
	}
6
}

Para mostrar ambos nombres, puedes escribir un ayudante de bloques que ejecute la plantilla una vez con el contexto del elemento primario y una vez con el contexto del elemento secundario. Este es el ayudante:

1
Handlebars.registerHelper("BothNames", function(context, options){
2
	return options.fn(context) + options.fn(context.Sub);
3
});

Y la plantilla tiene este aspecto:

1
{{#BothNames this}}
2
	<h2>{{Name}}</h2>
3
{{/BothName}}

La etiqueta hash antes del nombre del ayudante le dice a Handlebars que este es un ayudante de bloque, y cierras el bloque como lo harías con una etiqueta HTML. La función options.fn ejecuta la sección de la plantilla dentro del bloque con cualquier contexto que le proporciones.

Ya que tenemos los conceptos básicos, comencemos a crear una demostración completa.


Creación de una plantilla del sitio

No creas un sitio con Handlebars.

La plantilla que crearemos es para un sitio de recetas. Esto te dará una buena comprensión de Handlebars, ya que abarca obtener datos de una API y pasarlos a través de una plantilla.

Configurar un proyecto de Handlebars

Primero debemos cargar nuestro script de plantilla, pero para hacer eso, necesitamos crear un nuevo archivo HTML e incluir nuestra biblioteca Handlebars:

1
<html>
2
	<head>
3
		<title>Handlebars Demo</title>
4
		<script type="text/javascript" src="Handlebars.js"></script>
5
	</head>
6
	<body>
7
		<script id="Handlebars-Template" type="text/x-handlebars-template">
8
		</script>
9
	</body>
10
</html>

Para mayor comodidad, puedes almacenar tu plantilla en un elemento <script />  y cargarlo con JavaScript. Esto es mucho más limpio que almacenarlo directamente en una variable de JavaScript.

Ahora discutiremos cómo va a funcionar esta aplicación. En primer lugar, la aplicación se conecta a una API (estoy usando Yummly) para extraer información sobre algunas recetas. Después, pasamos esta información a Handlebars y la ejecutamos a través de la plantilla. Por último, reemplazamos la sección del cuerpo por el HTML recién generado. Es un proceso bastante sencillo; entonces, comencemos agregando un segundo bloque de script justo antes de la etiqueta body de cierre, y creemos una instancia de una variable Ajax:

1
<script>
2
3
var Ajax = (window.XMLHttpRequest) ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
4
5
Ajax.onreadystatechange = function(){
6
	if (Ajax.readyState == 4 && Ajax.status == 200)
7
	{
8
		//Parse the JSON data

9
		var RecipeData = JSON.parse(Ajax.responseText);
10
		
11
		//Get the Template from above

12
		var Source = document.getElementById("Handlebars-Template").textContent;
13
		
14
		//Compile the actual Template file

15
		var Template = Handlebars.compile(Source);
16
		
17
		//Generate some HTML code from the compiled Template

18
		var HTML = Template({ Recipes : RecipeData });
19
		
20
		//Replace the body section with the new code.

21
		document.body.innerHTML = HTML;
22
	}
23
}
24
25
Ajax.open("GET","Recipe.php", true);
26
Ajax.send();
27
28
</script>

Si los datos de tu sitio cambian regularmente, es posible que quieras echarle un vistazo a Handlebars.

Este es el código completo para compilar y generar código HTML a partir de una plantilla. Técnicamente, puede pasar los datos JSON de la API directamente a Handlebars, pero te encuentras con problemas de origen cruzado. En lugar de realizar algún tipo de hackeo o usar PHP para "hacer eco" de los datos en una variable de JavaScript, decidí poner todo eso en un archivo separado: Recipe.php. Entonces, antes de comenzar a construir la plantilla, revisemos ese archivo PHP.

Obteniendo los datos

La API de Yummly es bastante simple. No existe un sistema de autenticación elaborado; solo tienes que registrarte, obtener algunas credenciales e insertarlas en la URL. Puedes hacer eco directamente de los datos si lo quieres, pero quiero información un poco más detallada sobre cada receta. Por lo tanto, procesaré los datos de la primera llamada a la API y realizaré una segunda solicitud en cada receta. Este es el script PHP completo:

1
<?php
2
	//Empty Array to hold all the recipes

3
	$Json = [];
4
	
5
	$UserID = //Your ID Here;

6
	
7
	$UserKey = //Your Yummly key;

8
	
9
	//This searches Yummly for cake recipes

10
	$Recipes = file_get_contents("http://api.yummly.com/v1/api/recipes?_app_id=" . $UserID . "&_app_key=" . $UserKey . "&maxResult=2&requirePictures=true&q=Cake");
11
	
12
	//Decode the JSON into a php object

13
	$Recipes = json_decode($Recipes)->matches;
14
	
15
	
16
	//Cycle Through The Recipes and Get full recipe for each

17
	foreach($Recipes as $Recipe)
18
	{
19
		$ID = $Recipe->id; 
20
		$R = json_decode(file_get_contents("http://api.yummly.com/v1/api/recipe/" . $ID . "?_app_id=" . $UserID . "&_app_key=" . $UserKey . "&images=large"));
21
		
22
		
23
		//This is the data we are going to pass to our Template

24
		array_push($Json, array(
25
			Name => $R->name,
26
			Ingredients => $R->ingredientLines,
27
			Image => $R->images[0]->hostedLargeUrl,
28
			Yield => $R->yield,
29
			Flavors => $R->flavors,
30
			Source => array(
31
				Name => $R->source->sourceDisplayName,
32
				Url => $R->source->sourceRecipeUrl
33
			)
34
		));
35
	}
36
	
37
	//Print out the final JSON object

38
	echo json_encode($Json);
39
?>

Al crear tu sitio con una plantilla de Handlebars, puedes producir el código de un sitio completo en solo unas pocas líneas. Aquí está toda la plantilla:

1
<script id="Handlebars-Template" type="text/x-handlebars-template">
2
	<div id="Content">
3
	  <h1>&Xi;RecipeCards 
4
	  	<span id='BOS'>Recipe search powered by 
5
	  		<a id='Logo' href='http://www.yummly.com/recipes'>
6
	  			<img src='http://static.yummly.com/api-logo.png'/>
7
	  		</a>

8
	  	</span>

9
	  </h1>

10
	  {{#each Recipes}}
11
	  	<div class='Box'>
12
		  	<img class='Thumb' src="{{{Image}}}" alt="{{Name}}">
13
		  	<h3>{{Name}} <a id='Logo' href="{{Source.Url}}"> - {{Source.Name}}</a></h3>
14
		  	<h5>{{getFlavor Flavors}}</h5>

15
		  	<h5>{{Yield}}</h5>

16
		  	<p>Ingredients:</p>

17
		  	<ul>
18
		  		{{#each Ingredients}}
19
		  			<li>{{this}}</li>

20
		  		{{/each}}
21
		  	</ul>

22
	  	</div>

23
	  {{/each}}
24
	</div>

25
</script>

Repasemos este código. Las primeras siete líneas son solo el logotipo en la parte superior de la página. Luego, para cada receta, creamos una 'tarjeta' de recetas con una imagen, nombre e ingredientes. 

La API de Yummly devuelve una lista de datos de sabores (es decir, qué tan dulce, agrio, picante, etcétera...) para cada artículo. Escribí una función auxiliar, llamada getFlavor, que toma esta información y devuelve el sabor más dominante del plato. Para que esta plantilla funcione, necesitamos cargar el ayudante getFlavor en Handlebars antes de analizar la plantilla. Entonces, al comienzo de la segunda sección del script, agrega el siguiente código antes del código Ajax:

1
Handlebars.registerHelper("getFlavor", function(FlavorsArr){
2
	var H = 0;
3
	var Name = '';
4
	for(var F in FlavorsArr)
5
	{
6
		if(FlavorsArr[F] > H)
7
		{
8
			H = FlavorsArr[F];
9
			Name = F;
10
		}
11
	}
12
	return "This Dish has a " + Name + " Flavor";
13
});

Ahora, cada vez que Handlebars ve getFlavor, llama a la función asociada y recupera la información de sabor.

En este punto, puedes jugar y diseñar la plantilla como quieras, pero lo más probable es que veas que este proceso es lento. Esto se debe principalmente a las tres llamadas a la API antes de que Handlebars cargue la página. Obviamente, esto no es ideal, pero la compilación previa de tu plantilla puede ayudar.


Precompilación

Tienes dos opciones diferentes, cuando se trata de Handlebars. La primera es simplemente precompilar la plantilla real. Esto reduce el tiempo de carga y no tendrás que incluir el compilador de Handlebars en tu página.

Esto también da como resultado un tamaño de archivo más pequeño, pero esto realmente no ayuda en nuestro escenario.

Nuestro problema es la comunicación entre el navegador y la API. Si quieres precompilar tu plantilla, puedes descargar el paquete Node.js a través de npm con el siguiente comando:

1
npm install handlebars -g

Es posible que debas hacer esto como raíz (es decir, agregar 'sudo' antes del comando). Una vez instalado, puedes crear un archivo para tu plantilla y compilarlo así:

1
handlebars demo.handlebars -f demo.js

Debes darle a tu archivo de plantilla una extensión .handlebars. Esto no es obligatorio, pero si lo nombras como demo.html, entonces el nombre de la plantilla será "demo.html" en lugar de simplemente "demo". Después de nombrar tu plantilla, simplemente incluye el archivo de salida junto con la versión en tiempo de ejecución de Handlebars (puedes usar la versión normal, pero es más grande) y escribe lo siguiente:

1
var template = Handlebars.templates['demo'];
2
var html = template({ Your Json Data Here });

La declaración unless es... esencialmente una declaración if invertida.

Pero, como mencioné antes, esto realmente no nos ayuda en este escenario. ¿Qué podemos hacer entonces? Bueno, podemos precompilar y generar todo el archivo. Esto hace que podamos ejecutar la plantilla con datos y guardar la salida HTML final; en otras palabras, almacenamiento en caché. Esto acelera drásticamente el tiempo de carga de la aplicación. Desafortunadamente, JavaScript del lado del cliente no tiene capacidades de E/S de archivos. Entonces, la forma más fácil de lograr esto es simplemente enviar el HTML a un cuadro de texto y guardarlo manualmente. Ten en cuenta las directrices de una API sobre el almacenamiento en caché. La mayoría de las API tienen una cantidad máxima de tiempo durante el cual se pueden almacenar los datos en la caché; asegúrate de encontrar esa información antes de guardar páginas estáticas.


Conclusión

Esta fue una introducción rápida a Handlebars. En el futuro, puedes buscar en "Parciales": pequeñas plantillas que se pueden usar como funciones. Como siempre, no dudes en dejar un comentario o una pregunta en la sección de comentarios.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.