handlebars.java – jknack
Logic-less and semantic Mustache templates with Java
关键指标一览
主题标签
README 详细介绍



Handlebars.java
===============
Logic-less and semantic Mustache templates with Java
Handlebars handlebars = new Handlebars();
Template template = handlebars.compileInline("Hello {{this}}!");
System.out.println(template.apply("Handlebars.java"));
Output:
Hello Handlebars.java!
Handlebars.java is a Java port of handlebars.
Handlebars provides the power necessary to let you build semantic templates effectively with no frustration.
Mustache templates are compatible with Handlebars, so you can take a Mustache template, import it into Handlebars, and start taking advantage of the extra Handlebars features.
Requirements
- Handlebars 4.4+ requires Java 17 or higher.
- Handlebars 4.3+ requires Java 8 or higher (NOT MAINTAINED).
Getting Started
In general, the syntax of Handlebars templates is a superset of Mustache templates. For basic syntax, check out the Mustache manpage.
The Handlebars.java blog is a good place for getting started too. Javadoc is available at javadoc.io.
Maven
Stable version: 
<dependency>
<groupId>com.github.jknack</groupId>
<artifactId>handlebars</artifactId>
<version>${handlebars-version}</version>
</dependency>
Loading templates
Templates are loaded using the ``TemplateLoader` class. Handlebars.java provides three implementations of a ``TemplateLoader
* ClassPathTemplateLoader (default)
* FileTemplateLoader
* SpringTemplateLoader (see the [handlebars-springmvc](https://github.com/jknack/handlebars.java/tree/master/handlebars-springmvc) module)
This example loadsmytemplate.hbs
mytemplate.hbs:Hello {{this}}!
javavar handlebars = new Handlebars();
var template = handlebars.compile("mytemplate");
System.out.println(template.apply("Handlebars.java"));
```
Output:
Hello Handlebars.java!
You can specify a different ```TemplateLoader
javaTemplateLoader loader = ...;
Handlebars handlebars = new Handlebars(loader);
#### Templates prefix and suffix
ATemplateLoader *prefix *suffix``: useful for setting a default suffix or file extension for your templates. Default is: ``.hbs
Example:javaTemplateLoader loader = new ClassPathTemplateLoader();
loader.setPrefix("/templates");
loader.setSuffix(".html");
Handlebars handlebars = new Handlebars(loader);
Template template = handlebars.compile("mytemplate");
System.out.println(template.apply("Handlebars.java"));
Handlebars.java will resolvemytemplate`` to ``/templates/mytemplate.html
## The Handlebars.java Server
The handlebars.java server is small application where you can write Mustache/Handlebars template and merge them with data.
It is a useful tool for Web Designers.
Download from Maven Central:
1. Go [here](http://search.maven.org/#search%7Cga%7C1%7Chandlebars-proto)
2. Under the **Download** section click on **jar**
Maven:xml
com.github.jknack
handlebars-proto
${current-version}
Usage:java -jar handlebars-proto-${current-version}.jar -dir myTemplates
Example:
**myTemplates/home.hbs**{{#items}}
{{name}}
{{/items}}
**myTemplates/home.json**json{
"items": [
{
"name": "Handlebars.java rocks!"
}
]
}
or if you prefer YAML **myTemplates/home.yml**:ymlitems:
- name: Handlebars.java rocks!
### Open a browser a type:http://localhost:6780/home.hbs
enjoy it!
### Additional options:
* -dir: set the template directory
* -prefix: set the template's prefix, default is /
* -suffix: set the template's suffix, default is .hbs
* -context: set the context's path, default is /
* -port: set port number, default is 6780
* -content-type: set the content-type header, default is text/html
### Multiple data sources per template
Sometimes you need or want to test multiple datasets over a single template, you can do that by setting adata
Example:http://localhost:6780/home.hbs?data=mytestdata
Please note you don't have to specify the extension file.
## Helpers
### Built-in helpers:
* **with**
* **each**
* **if**
* **unless**
* **log**
* **block**
* **partial**
* **precompile**
* **embedded**
* **i18n** and **i18nJs**
* **string helpers**
* **conditional helpers**
### with, each, if, unless:
See the [built-in helper documentation](https://handlebarsjs.com/guide/block-helpers.html).
### block and partial
Block and partial helpers work together to provide you [Template Inheritance](http://jknack.github.io/handlebars.java/reuse.html).
Usage:{{#block "title"}}
...
{{/block}}
context: A string literal which defines the region's name.
Usage:{{#partial "title"}}
...
{{/partial}}
context: A string literal which defines the region's name.
### precompile
Precompile a Handlebars.java template to JavaScript using handlebars.js
user.hbshtmlHello {{this}}!
home.hbshtml
{{precompile "user"}}
Output:html
(function() {
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
templates['user'] = template(function (Handlebars,depth0,helpers,partials,data) {
helpers = helpers || Handlebars.helpers;
var buffer = "", functionType="function", escapeExpression=this.escapeExpression;
buffer += "Hi ";
depth0 = typeof depth0 === functionType ? depth0() : depth0;
buffer += escapeExpression(depth0) + "!";
return buffer;});
})();
You can access the precompiled template with:jsvar template = Handlebars.templates['user']
By default it uses:/handlebars-v1.3.0.jsjavaHandlebars handlebars = new Handlebars();
handlebars.handlebarsJsFile("/handlebars-v2.0.0.js");
For more information have a look at the [Precompiling Templates](https://github.com/wycats/handlebars.js/) documentation.
Usage:{{precompile "template" [wrapper="anonymous, amd or none"]}}
context: A template name. Required.
wrapper: One of "anonymous", "amd" or "none". Default is: "anonymous"
There is a [maven plugin](https://github.com/jknack/handlebars.java/tree/master/handlebars-maven-plugin) available too.
### embedded
The embedded helper allow you to "embedded" a handlebars template inside a
user.hbshtml
home.hbshtml
...
{{embedded "user"}}
...
Output:html
...
...
Usage:{{embedded "template"}}
context: A template name. Required.
### i18n
A helper built on top of a {@link ResourceBundle}. A {@link ResourceBundle} is the most well known mechanism for internationalization (i18n) in Java.
Usage:html{{i18n "hello"}}
This require amessages.properties
Using a locale:html{{i18n "hello" locale="es_AR"}}
This requires amessages_es_AR.properties
Using a different bundle:html{{i18n "hello" bundle="myMessages"}}
This requires amyMessages.properties
Using a message format:html{{i18n "hello" "Handlebars.java"}}
Wherehello`` is `Hola {0}!`, results in ``Hola Handlebars.java!
### i18nJs
Translate aResourceBundle
Usage:{{i18nJs [locale] [bundle=messages]}}
If the locale argument is present it will translate that locale to JavaScript. Otherwise, it will use the default locale.
The generated code looks like this:javascript
I18n.defaultLocale = 'es_AR';
I18n.locale = 'es_AR';
I18n.translations = I18n.translations || {};
// Spanish (Argentina)
I18n.translations['es_AR'] = {
"hello": "Hi {{arg0}}!"
}
Finally, it converts message patterns like:Hi {0}`` into ``Hi {{arg0}}
### string helpers
Functions like abbreviate, capitalize, join, dateFormat, yesno, etc., are available from [StringHelpers](https://github.com/jknack/handlebars.java/blob/master/handlebars/src/main/java/com/github/jknack/handlebars/helper/StringHelpers.java).
> NOTE: You need to register string helpers (they are not added by default)
### conditional helpers
Functions like eq, neq, lt, gt, and, or, not, etc., are available from [ConditionalHelpers](https://github.com/jknack/handlebars.java/blob/master/handlebars/src/main/java/com/github/jknack/handlebars/helper/ConditionalHelpers.java).
> NOTE: You need to register conditional helpers (they are not added by default)
### TypeSafe Templates
TypeSafe templates are created by extending theTypeSafeTemplatejava
// 1
public static interface UserTemplate extends TypeSafeTemplate {
// 2
public UserTemplate setAge(int age);
public UserTemplate setRole(String role);
}
// 3
UserTemplate userTmpl = handlebars.compileInline("{{name}} is {{age}} years old!")
.as(UserTemplate.class);
userTmpl.setAge(32);
assertEquals("Edgar is 32 years old!", userTmpl.apply(new User("Edgar")));
1. You extend theTypeSafeTemplate 2. You add all the set method you need. The set method can returnsvoid`` or ``TypeSafeTemplate 3. You create a new type safe template using the:as()
### Registering Helpers
There are two ways of registering helpers.
#### Using theHelperjavahandlebars.registerHelper("blog", new Helper() {
public CharSequence apply(Blog blog, Options options) {
return options.fn(blog);
}
});
javahandlebars.registerHelper("blog-list", new Helper<List>() {
public CharSequence apply(List list, Options options) {
String ret = "
- ";
- " + options.fn(blog) + " ";
for (Blog blog: list) {
ret += "
}
return new Handlebars.SafeString(ret + "
}
});
#### Using aHelperSourceA helper source is any class with public methods returning an instance of aCharSequencejavapublic static? CharSequence methodName(context?, parameter*, options?) {
}
Where:
* A method can/can't be static
* The method's name becomes the helper's name
* Context, parameters and options are all optionals
* If context and options are present they must be the **first** and **last** arguments of the method
All these are valid definitions of helper methods:javapublic class HelperSource {
public String blog(Blog blog, Options options) {
return options.fn(blog);
}
public static String now() {
return new Date().toString();
}
public String render(Blog context, String param0, int param1, boolean param2, Options options) {
return ...
}
}
...
handlebars.registerHelpers(new HelperSource());
Or, if you prefer static methods only:javahandlebars.registerHelpers(HelperSource.class);
#### With plainJavaScriptThat's right since1.1.0
helpers.js:javascriptHandlebars.registerHelper('hello', function (context) {
return 'Hello ' + context;
})
javahandlebars.registerHelpers(new File("helpers.js"));
Cool, isn't?
### Helper Options
#### Parametersjavahandlebars.registerHelper("blog-list", new Helper() {
public CharSequence apply(List list, Options options) {
String p0 = options.param(0);
assertEquals("param0", p0);
Integer p1 = options.param(1);
assertEquals(123, p1);
...
}
});
Bean bean = new Bean();
bean.setParam1(123);
Template template = handlebars.compileInline("{{#blog-list blogs "param0" param1}}{{/blog-list}}");
template.apply(bean);
#### Default parametersjavahandlebars.registerHelper("blog-list", new Helper() {
public CharSequence apply(List list, Options options) {
String p0 = options.param(0, "param0");
assertEquals("param0", p0);
Integer p1 = options.param(1, 123);
assertEquals(123, p1);
...
}
});
Template template = handlebars.compileInline("{{#blog-list blogs}}{{/blog-list}}");
#### Hashjavahandlebars.registerHelper("blog-list", new Helper() {
public CharSequence apply(List list, Options options) {
String classParam = options.hash("class");
assertEquals("blog-css", classParam);
...
}
});
handlebars.compileInline("{{#blog-list blogs class="blog-css"}}{{/blog-list}}");
#### Default hashjavahandlebars.registerHelper("blog-list", new Helper() {
public CharSequence apply(List list, Options options) {
String class = options.hash("class", "blog-css");
assertEquals("blog-css", class);
...
}
});
handlebars.compileInline("{{#blog-list blogs}}{{/blog-list}}");
## Error reporting
### Syntax errorsfile:line:column: message
evidence
^
[at file:line:column]
Examples:
template.hbs{{value
/templates.hbs:1:8: found 'eof', expected: 'id', 'parameter', 'hash' or '}'
{{value
^
If a partial isn't found or if it has errors, a call stack is added:/deep1.hbs:1:5: The partial '/deep2.hbs' could not be found
{{> deep2
^
at /deep1.hbs:1:10
at /deep.hbs:1:10
### Helper/Runtime errors
Helper or runtime errors are similar to syntax errors, except for two things:
1. The location of the problem may (or may not) be the correct one
2. The stack-trace isn't available
Examples:
Block helper:javapublic CharSequence apply(final Object context, final Options options) throws IOException {
if (context == null) {
throw new IllegalArgumentException(
"found 'null', expected 'string'");
}
if (!(context instanceof String)) {
throw new IllegalArgumentException(
"found '" + context + "', expected 'string'");
}
...
}
base.hbs
{{#block}} {{/block}}
Handlebars.java reports:/base.hbs:2:4: found 'null', expected 'string'
{{#block}} ... {{/block}}
In short, from a helper you can throw an Exception and Handlebars.java will add the filename, line, column and the evidence.
## Advanced Usage
### Extending the context stack
Let's say you need to access to the current logged-in user in every single view/page.
You can publish the current logged in user by hooking into the context-stack. See it in action:javahookContextStack(Object model, Template template) {
User user = ....;// Get the logged-in user from somewhere
Map moreData = ...;
Context context = Context
.newBuilder(model)
.combine("user", user)
.combine(moreData)
.build();
template.apply(context);
context.destroy();
}
Where is thehookContextStack
### Using the ValueResolver
By default, Handlebars.java use the JavaBean methods (i.e. public getXxx and isXxx methods), Map as value and Method as value resolvers.
You can choose a different value resolver. This section describe how to do this.
#### The JavaBeanValueResolver
Resolves values from public methods prefixed with "get/is"javaContext context = Context
.newBuilder(model)
.resolver(JavaBeanValueResolver.INSTANCE)
.build();
#### The FieldValueResolver
Resolves values from no-static fields.javaContext context = Context
.newBuilder(model)
.resolver(FieldValueResolver.INSTANCE)
.build();
#### The MapValueResolver
Resolves values from ajava.util.MapjavaContext context = Context
.newBuilder(model)
.resolver(MapValueResolver.INSTANCE)
.build();
#### The MethodValueResolver
Resolves values from public methods.javaContext context = Context
.newBuilder(model)
.resolver(MethodValueResolver.INSTANCE)
.build();
#### The JsonNodeValueResolver
Resolves values fromJsonNodejavaContext context = Context
.newBuilder(model)
.resolver(JsonNodeValueResolver.INSTANCE)
.build();
Available in [Jackson 1.x](https://github.com/jknack/handlebars.java/tree/master/handlebars-json) and [Jackson 2.x](https://github.com/jknack/handlebars.java/tree/master/handlebars-jackson2) modules.
#### Using multiples value resolversjavaContext context = Context
.newBuilder(model)
.resolver(
MapValueResolver.INSTANCE,
JavaBeanValueResolver.INSTANCE,
FieldValueResolver.INSTANCE
).build();
### The Cache System
The cache system is designed to provide scalability and flexibility. Here is a quick view of theTemplateCachejavapublic interface TemplateCache {
/**
- Remove all mappings from the cache.
*/
void clear();
/**
- Evict the mapping for this source from this cache if it is present.
*
- @param source the source whose mapping is to be removed from the cache
*/
void evict(TemplateSource source);
/**
- Return the value to which this cache maps the specified key.
*
- @param source source whose associated template is to be returned.
- @param parser The Handlebars parser.
- @return A template.
- @throws IOException If input can't be parsed.
*/
Template get(TemplateSource source, Parser parser) throws IOException;
}
As you can see, there isn't aput`` method. All the hard work is done in the ``get
By default, Handlebars.java uses anullTemplate get(TemplateSource source, Parser parser) throws IOException {
return parser.parse(source);
}
In addition to thenull
1.ConcurrentMapTemplateCache``: a template cache implementation built on top of a `ConcurrentMapThis implementation works very well in general, but there is a small window where two or more threads can compile the same template. This isn't a huge problem with Handlebars.java because the compiler is very very fast.
But if for some reason you don't want this, you can use the
HighConcurrencyTemplateCache
2.
HighConcurrencyTemplateCache`: a template cache implementation built on top of `ConcurrentMapThis cache implementation eliminates the window created by
ConcurrentMapTemplateCache` to ``zeroIt follows the patterns described in [Java Concurrency in Practice](http://www.amazon.com/Java-Concurrency-Practice-Brian-Goetz/dp/0321349601) and ensures that a template will be compiled just once regardless of the number of threads.
3.GuavaTemplateCache
You can configure Handlebars.java to use a cache by:Handlebars hbs = new Handlebars()
.with(new MyCache());
### Using a MissingValueResolver (@deprecated)
NOTE: MissingValueResolver is available in 1.3.0
AMissingValueResolver`` let you use default values for `{{variable}}` expressions resolved to ``nulljavaMissingValueResolver missingValueResolver = new MissingValueResolver() {
public String resolve(Object context, String name) {
//return a default value or throw an exception
...;
}
};
Handlebars handlebars = new Handlebars().with(missingValueResolver);
### Helper Missing
By default, Handlebars.java throws anjava.lang.IllegalArgumentException() You can override the default behaviour by providing a special helper:helperMissingjavahandlebars.registerHelperMissing(new Helper