Templat dengan Handlebars

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan melihat perpustakaan Handlebars.java untuk pengurusan templat yang mudah.

2. Pergantungan Maven

Mari mulakan dengan menambahkan kebergantungan setang :

 com.github.jknack handlebars 4.1.2 

3. Templat Ringkas

Templat Handlebars boleh menjadi apa-apa jenis fail teks. Ia terdiri daripada tag seperti {{name}} dan {{#each people}}.

Kemudian kami mengisi tag ini dengan melewati objek konteks, seperti Peta atau Objek lain .

3.1. Menggunakan ini

Untuk menyampaikan satu nilai String ke templat kita, kita dapat menggunakan Objek apa pun sebagai konteksnya. Kita juga mesti menggunakan {{ this}} dalam templat kita.

Kemudian Handlebars memanggil kaedah toString pada objek konteks dan menggantikan tag dengan hasilnya:

@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!"); }

Dalam contoh di atas, pertama-tama kami membuat contoh Handlebars, titik masuk API kami.

Kemudian, kami memberikan contoh itu templat kami. Di sini, kita hanya meneruskan templat sebaris, tetapi kita akan melihat sebentar lagi beberapa cara yang lebih hebat.

Akhirnya, kami memberikan templat yang disusun konteks kami. {{ini}} akhirnya akan memanggil ke String, itulah sebabnya kita melihat "Hai Baeldung!" .

3.2. Melewati Peta sebagai Objek Konteks

Kami baru melihat cara menghantar String untuk konteks kami, sekarang mari kita mencuba Peta :

@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!"); }

Sama seperti contoh sebelumnya, kami menyusun templat kami dan kemudian meneruskan objek konteks, tetapi kali ini sebagai Peta .

Juga, perhatikan bahawa kami menggunakan {{name}} dan bukan {{ini}} . Ini bermaksud peta kita mesti mengandungi kunci, nama .

3.3. Melewati Objek Khusus sebagai Objek Konteks

Kami juga dapat meneruskan objek khusus ke templat kami:

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; } }

Dengan menggunakan kelas Person , kami akan memperoleh hasil yang sama seperti contoh sebelumnya:

@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}} dalam templat kami akan menyelidiki objek Person kami dan mendapat nilai bidang nama .

4. Pemuat Templat

Setakat ini, kami telah menggunakan templat yang ditentukan dalam kod. Namun, itu bukan satu-satunya pilihan. Kami juga dapat membaca templat dari fail teks.

Handlebars.java memberikan sokongan khas untuk membaca templat dari classpath, sistem fail atau konteks servlet. Secara lalai, Handlebars mengimbas classpath untuk memuat templat yang diberikan:

@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!"); }

Oleh itu, kerana kami memanggil compile dan bukannya compileInline, ini adalah petunjuk kepada Handlebars untuk mencari /greeting.hbs di classpath.

Walau bagaimanapun, kami juga boleh mengkonfigurasi sifat ini dengan 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 }

Dalam kes ini, kami meminta Handlebars untuk mencari /handlebars/greeting.html di classpath .

Finally, we can chain multiple TemplateLoader instances:

@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 }

So, here, we've got two loaders, and that means Handlebars will search two directories for the greeting template.

5. Built-in Helpers

Built-in helpers provide us additional functionality when writing our templates.

5.1. with Helper

The with helper changes the current context:

{{#with address}} 

I live in {{street}}

{{/with}}

In our sample template, the {{#with address}} tag starts the section and the {{/with}} tag ends it.

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

Akhir sekali, yang jika pembantu menyediakan rendering bersyarat .

{{#if busy}} 

{{name}} is busy.

{{else}}

{{name}} is not busy.

{{/if}}

Dalam templat kami, kami memberikan mesej yang berbeza mengikut bidang sibuk .

@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.

"); }

Setelah menyusun templat, kami menetapkan objek konteks. Oleh kerana bidang sibuk adalah benar , hasil akhir menjadi

Baeldung sibuk.

.

6. Pembantu Templat Tersuai

Kami juga boleh membuat pembantu khas kami sendiri.

6.1. Penolong

Antara muka Helper membolehkan kita membuat templat penolong.

Sebagai langkah pertama, kita mesti menyediakan pelaksanaan 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.

Kami juga menyiasat pembantu templat yang memberikan banyak fungsi. Terakhir, kami melihat pelbagai cara untuk menggunakan semula templat kami.

Akhirnya, periksa kod sumber untuk semua contoh di GitHub.