1. Información general
En este tutorial, analizaremos la biblioteca Handlebars.java para una fácil administración de plantillas.
2. Dependencias de Maven
Comencemos agregando la dependencia del manillar :
com.github.jknack handlebars 4.1.2
3. Una plantilla sencilla
Una plantilla de manubrios puede ser cualquier tipo de archivo de texto. Consiste en etiquetas como {{name}} y {{#each people}}.
Luego, completamos estas etiquetas pasando un objeto de contexto, como un mapa u otro objeto.
3.1. Usando esto
Para pasar un solo valor de cadena a nuestra plantilla, podemos usar cualquier objeto como contexto. También debemos usar la etiqueta {{ this}} en nuestra plantilla.
Luego, Handlebars llama al método toString en el objeto de contexto y reemplaza la etiqueta con el resultado:
@Test public void whenThereIsNoTemplateFile_ThenCompilesInline() throws IOException { Handlebars handlebars = new Handlebars(); Template template = handlebars.compileInline("Hi {{this}}!"); String templateString = template.apply("Baeldung"); assertThat(templateString).isEqualTo("Hi Baeldung!"); }
En el ejemplo anterior, primero creamos una instancia de Handlebars, nuestro punto de entrada de API.
Luego, le damos a esa instancia nuestra plantilla. Aquí, simplemente pasamos la plantilla en línea, pero veremos en un momento algunas formas más poderosas.
Finalmente, le damos a la plantilla compilada nuestro contexto. {{this}} simplemente va a terminar llamando a toString, por eso vemos "¡Hola Baeldung!" .
3.2. Pasar un mapa como objeto de contexto
Acabamos de ver cómo enviar una cadena para nuestro contexto, ahora probemos un mapa :
@Test public void whenParameterMapIsSupplied_thenDisplays() throws IOException { Handlebars handlebars = new Handlebars(); Template template = handlebars.compileInline("Hi {{name}}!"); Map parameterMap = new HashMap(); parameterMap.put("name", "Baeldung"); String templateString = template.apply(parameterMap); assertThat(templateString).isEqualTo("Hi Baeldung!"); }
Al igual que en el ejemplo anterior, estamos compilando nuestra plantilla y luego pasando el objeto de contexto, pero esta vez como un mapa .
Además, observe que estamos usando {{name}} en lugar de {{this}} . Esto significa que nuestro mapa debe contener la clave, nombre .
3.3. Pasar un objeto personalizado como objeto de contexto
También podemos pasar un objeto personalizado a nuestra plantilla:
public class Person { private String name; private boolean busy; private Address address = new Address(); private List friends = new ArrayList(); public static class Address { private String street; } }
Usando la clase Person , lograremos el mismo resultado que en el ejemplo anterior:
@Test public void whenParameterObjectIsSupplied_ThenDisplays() throws IOException { Handlebars handlebars = new Handlebars(); Template template = handlebars.compileInline("Hi {{name}}!"); Person person = new Person(); person.setName("Baeldung"); String templateString = template.apply(person); assertThat(templateString).isEqualTo("Hi Baeldung!"); }
{{name}} en nuestra plantilla profundizará en nuestro objeto Person y obtendrá el valor del campo de nombre .
4. Cargadores de plantillas
Hasta ahora, hemos utilizado plantillas que se definen dentro del código. Sin embargo, no es la única opción. También podemos leer plantillas de archivos de texto.
Handlebars.java proporciona un soporte especial para leer plantillas de classpath, sistema de archivos o contexto de servlet. Por defecto, Handlebars escanea la ruta de clases para cargar la plantilla dada:
@Test public void whenNoLoaderIsGiven_ThenSearchesClasspath() throws IOException { Handlebars handlebars = new Handlebars(); Template template = handlebars.compile("greeting"); Person person = getPerson("Baeldung"); String templateString = template.apply(person); assertThat(templateString).isEqualTo("Hi Baeldung!"); }
Entonces, debido a que llamamos compile en lugar de compileInline, esta es una sugerencia para que Handlebars busque /greeting.hbs en la ruta de clases.
Sin embargo, también podemos configurar estas propiedades con ClassPathTemplateLoader :
@Test public void whenClasspathTemplateLoaderIsGiven_ThenSearchesClasspathWithPrefixSuffix() throws IOException { TemplateLoader loader = new ClassPathTemplateLoader("/handlebars", ".html"); Handlebars handlebars = new Handlebars(loader); Template template = handlebars.compile("greeting"); // ... same as before }
En este caso, le estamos diciendo a Handlebars que busque /handlebars/greeting.html en la ruta de clases .
Finalmente, podemos encadenar múltiples instancias de TemplateLoader :
@Test public void whenMultipleLoadersAreGiven_ThenSearchesSequentially() throws IOException { TemplateLoader firstLoader = new ClassPathTemplateLoader("/handlebars", ".html"); TemplateLoader secondLoader = new ClassPathTemplateLoader("/templates", ".html"); Handlebars handlebars = new Handlebars().with(firstLoader, secondLoader); // ... same as before }
Entonces, aquí, tenemos dos cargadores, y eso significa que Handlebars buscará dos directorios para la plantilla de saludo .
5. Ayudantes incorporados
Los ayudantes incorporados nos brindan funcionalidad adicional al escribir nuestras plantillas.
5.1. con ayudante
El with helper cambia el contexto actual :
{{#with address}} I live in {{street}}
{{/with}}
En nuestra plantilla de muestra, la etiqueta {{#with address}} comienza la sección y la etiqueta {{/ with}} la termina .
In essence, we're drilling into the current context object – let's say person – and setting address as the local context for the with section. Thereafter, every field reference in this section will be prepended by person.address.
So, the {{street}} tag will hold the value of person.address.street:
@Test public void whenUsedWith_ThenContextChanges() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); Template template = handlebars.compile("with"); Person person = getPerson("Baeldung"); person.getAddress().setStreet("World"); String templateString = template.apply(person); assertThat(templateString).contains("I live in World
"); }
We're compiling our template and assigning a Person instance as the context object. Notice that the Person class has an Address field. This is the field we're supplying to the with helper.
Though we went one level into our context object, it is perfectly fine to go deeper if the context object has several nested levels.
5.2. each Helper
The each helper iterates over a collection:
{{#each friends}} {{name}} is my friend. {{/each}}
As a result of starting and closing the iteration section with {{#each friends}} and {{/each}} tags, Handlebars will iterate over the friends field of the context object.
@Test public void whenUsedEach_ThenIterates() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); Template template = handlebars.compile("each"); Person person = getPerson("Baeldung"); Person friend1 = getPerson("Java"); Person friend2 = getPerson("Spring"); person.getFriends().add(friend1); person.getFriends().add(friend2); String templateString = template.apply(person); assertThat(templateString) .contains("Java is my friend.", "Spring is my friend."); }
In the example, we're assigning two Person instances to the friends field of the context object. So, Handlebars repeats the HTML part two times in the final output.
5.3. if Helper
Por último, el ayudante if proporciona una representación condicional .
{{#if busy}} {{name}} is busy.
{{else}} {{name}} is not busy.
{{/if}}
En nuestra plantilla, proporcionamos diferentes mensajes según el campo de ocupado .
@Test public void whenUsedIf_ThenPutsCondition() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); Template template = handlebars.compile("if"); Person person = getPerson("Baeldung"); person.setBusy(true); String templateString = template.apply(person); assertThat(templateString).contains("Baeldung is busy.
"); }
Después de compilar la plantilla, configuramos el objeto de contexto. Dado que el campo ocupado es verdadero , la salida final se convierte en
Baeldung está ocupado.
.6. Ayudantes de plantillas personalizadas
También podemos crear nuestros propios ayudantes personalizados.
6.1. Ayudante
La interfaz de ayuda nos permite crear una plantilla de ayuda.
Como primer paso, debemos proporcionar una implementación de Helper :
new Helper() { @Override public Object apply(Person context, Options options) throws IOException { String busyString = context.isBusy() ? "busy" : "available"; return context.getName() + " - " + busyString; } }
As we can see, the Helper interface has only one method which accepts the context and options objects. For our purposes, we'll output the name and busy fields of Person.
After creating the helper, we must also register our custom helper with Handlebars:
@Test public void whenHelperIsCreated_ThenCanRegister() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); handlebars.registerHelper("isBusy", new Helper() { @Override public Object apply(Person context, Options options) throws IOException { String busyString = context.isBusy() ? "busy" : "available"; return context.getName() + " - " + busyString; } }); // implementation details }
In our example, we're registering our helper under the name of isBusy using the Handlebars.registerHelper() method.
As the last step, we must define a tag in our template using the name of the helper:
{{#isBusy this}}{{/isBusy}}
Notice that each helper has a starting and ending tag.
6.2. Helper Methods
When we use the Helper interface, we can only create only one helper. In contrast, a helper source class enables us to define multiple template helpers.
Moreover, we don't need to implement any specific interface. We just write our helper methods in a class then HandleBars extracts helper definitions using reflection:
public class HelperSource { public String isBusy(Person context) { String busyString = context.isBusy() ? "busy" : "available"; return context.getName() + " - " + busyString; } // Other helper methods }
Since a helper source can contain multiple helper implementations, registration is different than the single helper registration:
@Test public void whenHelperSourceIsCreated_ThenCanRegister() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); handlebars.registerHelpers(new HelperSource()); // Implementation details }
We're registering our helpers using the Handlebars.registerHelpers() method. Moreover, the name of the helper method becomes the name of the helper tag.
7. Template Reuse
The Handlebars library provides several ways to reuse our existing templates.
7.1. Template Inclusion
Template inclusion is one of the approaches for reusing templates. It favors the composition of the templates.
Hi {{name}}!
This is the content of the header template – header.html.
In order to use it in another template, we must refer to the header template.
{{>header}} This is the page {{name}}
We have the page template – page.html – which includes the header template using {{>header}}.
When Handlebars.java processes the template, the final output will also contain the contents of header:
@Test public void whenOtherTemplateIsReferenced_ThenCanReuse() throws IOException { Handlebars handlebars = new Handlebars(templateLoader); Template template = handlebars.compile("page"); Person person = new Person(); person.setName("Baeldung"); String templateString = template.apply(person); assertThat(templateString) .contains("Hi Baeldung!
", "This is the page Baeldung
"); }
7.2. Template Inheritance
Alternatively to composition, Handlebars provides the template inheritance.
We can achieve inheritance relationships using the {{#block}} and {{#partial}} tags:
{{#block "intro"}} This is the intro {{/block}} {{#block "message"}} {{/block}}
By doing so, the messagebase template has two blocks – intro and message.
To apply inheritance, we need to override these blocks in other templates using {{#partial}}:
{{#partial "message" }} Hi there! {{/partial}} {{> messagebase}}
This is the simplemessage template. Notice that we're including the messagebase template and also overriding the message block.
8. Summary
In this tutorial, we've looked at Handlebars.java to create and manage templates.
We started with the basic tag usage and then looked at the different options to load the Handlebars templates.
También investigamos los ayudantes de plantillas que brindan una gran cantidad de funcionalidad. Por último, analizamos las diferentes formas de reutilizar nuestras plantillas.
Finalmente, consulte el código fuente para todos los ejemplos en GitHub.