1. Overview
1.概述
In this tutorial, we’ll look into the Handlebars.java library for easy template management.
在本教程中,我们将研究Handlebars.java库以方便模板管理。
2. Maven Dependencies
2.Maven的依赖性
Let’s start with adding the handlebars dependency:
让我们从添加handlebars依赖性开始。
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>4.1.2</version>
</dependency>
3. A Simple Template
3.一个简单的模板
A Handlebars template can be any kind of text file. It consists of tags like {{name}} and {{#each people}}.
Handlebars模板可以是任何类型的文本文件。它由{{name}}和{{#each people}}等标签组成。。
Then we fill in these tags by passing a context object, like a Map or other Object.
然后我们通过传递一个上下文对象,如Map或其他Object.来填充这些标签。
3.1. Using this
3.1.使用this
To pass a single String value to our template, we can use any Object as the context. We must also use the {{this}} tag in our template.
为了向我们的模板传递一个单一的String值,我们可以使用任何Object作为上下文。我们还必须在模板中使用{{this}} tag。
Then Handlebars calls the toString method on the context object and replaces the tag with the result:
然后Handlebars在上下文对象上调用toString 方法,并将标签替换为结果。
@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!");
}
In the above example, we first create an instance of Handlebars, our API entry point.
在上面的例子中,我们首先创建一个Handlebars的实例,我们的API入口。
Then, we give that instance our template. Here, we just pass the template inline, but we’ll see in a moment some more powerful ways.
然后,我们给这个实例提供我们的模板。在这里,我们只是内联地传递模板,但我们一会儿会看到一些更强大的方法。
Finally, we give the compiled template our context. {{this}} is just going to end up calling toString, which is why we see “Hi Baeldung!”.
最后,我们给编译后的模板提供上下文。{{this}}最终会调用toString,这就是为什么我们会看到“Hi Baeldung!最终将调用toString,这就是为什么我们看到“Hi Baeldung!”。
3.2. Passing a Map as Context Object
3.2.将Map作为上下文对象传递
We just saw how to send a String for our context, now let’s try a Map:
我们刚刚看到了如何为我们的上下文发送一个String,现在让我们试试一个Map。
@Test
public void whenParameterMapIsSupplied_thenDisplays() throws IOException {
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hi {{name}}!");
Map<String, String> parameterMap = new HashMap<>();
parameterMap.put("name", "Baeldung");
String templateString = template.apply(parameterMap);
assertThat(templateString).isEqualTo("Hi Baeldung!");
}
Similar to the previous example, we’re compiling our template and then passing the context object, but this time as a Map.
与之前的例子类似,我们正在编译我们的模板,然后传递上下文对象,但这次是作为一个Map。
Also, notice that we’re using {{name}} instead of {{this}}. This means that our map must contain the key, name.
另外,注意我们使用的是{{name}}而不是{this}}。{this}}代替了{this}}。这意味着我们的地图必须包含键,name。
3.3. Passing a Custom Object as Context Object
3.3.将一个自定义对象作为上下文对象传递
We can also pass a custom object to our template:
我们也可以向我们的模板传递一个自定义对象:。
public class Person {
private String name;
private boolean busy;
private Address address = new Address();
private List<Person> friends = new ArrayList<>();
public static class Address {
private String street;
}
}
Using the Person class, we’ll achieve the same result as the previous example:
使用Person 类,我们将实现与前面例子相同的结果。
@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}} in our template will drill into our Person object and get the value of the name field.
{{name}}在我们的模板中,将钻入我们的Person对象,获得name字段的值。在我们的模板中,将钻入我们的Person对象,并获得name字段的值。
4. Template Loaders
4.模板加载器
So far, we’ve used templates that are defined inside the code. However, it is not the only option. We can also read templates from text files.
到目前为止,我们已经使用了在代码内部定义的模板。然而,这并不是唯一的选择。我们也可以从文本文件中读取模板。
Handlebars.java provides special support for reading templates from the classpath, filesystem or servlet context. By default, Handlebars scans the classpath to load the given template:
Handlebars.java为从classpath、文件系统或servlet上下文读取模板提供了特殊支持。默认情况下,Handlebars扫描classpath以加载给定的模板:。
@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!");
}
So, because we called compile instead of compileInline, this is a hint to Handlebars to look for /greeting.hbs on the classpath.
因此,由于我们调用了compile而不是compileInline,这是给Handlebars的一个提示,即在classpath上寻找/greeting.hbs。
However, we can also configure these properties with ClassPathTemplateLoader:
然而,我们也可以用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
}
In this case, we’re telling Handlebars to look for the /handlebars/greeting.html on the classpath.
在这种情况下,我们要告诉Handlebars去寻找classpath上的/handlebars/greeting.html。
Finally, we can chain multiple TemplateLoader instances:
最后,我们可以连锁多个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
}
So, here, we’ve got two loaders, and that means Handlebars will search two directories for the greeting template.
所以,在这里,我们有两个加载器,这意味着Handlebars将在两个目录中搜索greeting模板。
5. Built-in Helpers
5.内置帮助器
Built-in helpers provide us additional functionality when writing our templates.
在编写模板时,内置帮助器为我们提供了额外的功能。
5.1. with Helper
5.1.与帮助器
The with helper changes the current context:
withhelper改变了当前的环境。
{{#with address}}
<h4>I live in {{street}}</h4>
{{/with}}
In our sample template, the {{#with address}} tag starts the section and the {{/with}} tag ends it.
在我们的示例模板中,{{#with address}}标签开始了这一节。标签开始该部分,而{{/with}}标签则结束该部分。标签结束它.。
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.
实质上,我们正在钻研当前的上下文对象–比方说person–并将address作为with部分的本地上下文。此后,本节中的每个字段引用都将以person.address为前缀。
So, the {{street}} tag will hold the value of person.address.street:
因此,{{street}}标签将持有person.address.street的值。tag将持有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("<h4>I live in World</h4>");
}
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.
我们正在编译我们的模板并分配一个Person实例作为上下文对象。注意,Person类有一个Address字段。这就是我们要提供给with助手的字段。
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
5.2.each帮助器
The each helper iterates over a collection:
each帮助器对一个集合进行迭代。
{{#each friends}}
<span>{{name}} is my friend.</span>
{{/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.
由于用{{#each friends}}和{{/each}}标签开始和结束迭代部分,Handlebars将对上下文对象的friends字段进行迭代。
@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("<span>Java is my friend.</span>", "<span>Spring is my friend.</span>");
}
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.
在这个例子中,我们将两个Person实例分配给上下文对象的friends字段。所以,Handlebars在最后的输出中重复了两次HTML部分。
5.3. if Helper
5.3.if帮助器
Lastly, the if helper provides conditional rendering.
最后,if帮助器提供了条件性渲染。
{{#if busy}}
<h4>{{name}} is busy.</h4>
{{else}}
<h4>{{name}} is not busy.</h4>
{{/if}}
In our template, we’re providing different messages according to the busy field.
在我们的模板中,我们根据busy字段提供不同的信息。
@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("<h4>Baeldung is busy.</h4>");
}
After compiling the template, we’re setting the context object. Since the busy field is true, the final output becomes <h4>Baeldung is busy.</h4>.
编译完模板后,我们要设置上下文对象。由于busy字段是true,最终的输出变成<h4>Baeldung is busy.</h4>。
6. Custom Template Helpers
6.自定义模板帮助器
We can also create our own custom helpers.
我们还可以创建我们自己的自定义帮助器。
6.1. Helper
6.1.帮助器
The Helper interface enables us to create a template helper.
Helper接口使我们能够创建一个模板帮助器。
As the first step, we must provide an implementation of Helper:
作为第一步,我们必须提供一个Helper的实现。
new Helper<Person>() {
@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.
我们可以看到,Helper接口只有一个方法,它接受context和options对象。为了我们的目的,我们将输出Person的name和busy字段。
After creating the helper, we must also register our custom helper with Handlebars:
在创建帮助器之后,我们还必须在Handlebars注册我们的自定义帮助器。
@Test
public void whenHelperIsCreated_ThenCanRegister() throws IOException {
Handlebars handlebars = new Handlebars(templateLoader);
handlebars.registerHelper("isBusy", new Helper<Person>() {
@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.
在我们的例子中,我们使用Handlebars.registerHelper()方法,以isBusy的名字注册了我们的助手。
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
6.2.帮助者方法
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.
当我们使用Helper接口时,我们只能创建一个帮助器。相反,一个帮助器源类使我们能够定义多个模板帮助器。
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:
此外,我们不需要实现任何特定的接口。我们只需在一个类中写下我们的辅助方法,然后HandleBars使用反射来提取辅助定义。
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.
我们使用Handlebars.registerHelpers()方法来注册我们的帮助器。此外,帮助器方法的名称成为帮助器标签的名称。
7. Template Reuse
7.模板重复使用
The Handlebars library provides several ways to reuse our existing templates.
Handlebars库提供了几种方法来重复使用我们现有的模板。
7.1. Template Inclusion
7.1.模板包含在内
Template inclusion is one of the approaches for reusing templates. It favors the composition of the templates.
模板包容是重复使用模板的方法之一。它倾向于模板的组合。
<h4>Hi {{name}}!</h4>
This is the content of the header template – header.html.
这是标题模板的内容 – header.html.。
In order to use it in another template, we must refer to the header template.
为了在另一个模板中使用它,我们必须参考header模板。
{{>header}}
<p>This is the page {{name}}</p>
We have the page template – page.html – which includes the header template using {{>header}}.
我们有page template – page.html – ,它包括使用{{>header}}的header模板。
When Handlebars.java processes the template, the final output will also contain the contents of header:
当Handlebars.java处理模板时,最终输出也将包含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("<h4>Hi Baeldung!</h4>", "<p>This is the page Baeldung</p>");
}
7.2. Template Inheritance
7.2.模板的继承性
Alternatively to composition, Handlebars provides the template inheritance.
作为组成的替代,Handlebars提供了模板继承。
We can achieve inheritance relationships using the {{#block}} and {{#partial}} tags:
我们可以使用{{#block}}和{{#partial}}标签实现继承关系。
<html>
<body>
{{#block "intro"}}
This is the intro
{{/block}}
{{#block "message"}}
{{/block}}
</body>
</html>
By doing so, the messagebase template has two blocks – intro and message.
通过这样做,messagebase模板有两个区块 – intro和message。
To apply inheritance, we need to override these blocks in other templates using {{#partial}}:
为了应用继承,我们需要在其他模板中使用{{#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.
这是simplemessage模板。注意,我们包括了messagebase模板,也覆盖了message块。
8. Summary
8.摘要
In this tutorial, we’ve looked at Handlebars.java to create and manage templates.
在本教程中,我们已经看了Handlebars.java来创建和管理模板。
We started with the basic tag usage and then looked at the different options to load the Handlebars templates.
我们从基本的标签使用开始,然后看了加载Handlebars模板的不同选项。
We also investigated the template helpers which provide a great deal of functionality. Lastly, we looked at the different ways to reuse our templates.
我们还调查了模板助手,它提供了大量的功能。最后,我们研究了重复使用模板的不同方法。
Finally, check out the source code for all examples over on GitHub.
最后,请在GitHub上查看所有例子的源代码,。