Handlebar / Mustache

com.github.jknack.handlebarsMustache

Basic Usage: hash + template -> render

Variable:

  • {{this}} - the context object, and no search to the parent and ancestors in the context stack
  • {{name}} - HTML escaped by default;
  • by default variable miss returns empty string

Unescaped: {{{name}}} or {{& name }}

Section: {{#boolean_val}} {{/boolean_val}}

  • render 0, 1 or many times
    • false, or empty lists : no render
    • non-empty list: one or more times, context of block set for each of items
    • callable object (lambdas): lambda called with (text, render) - javascript?
    • non-false, not a list: the object is used as context for single rendering

Inverted Sections {{^key}} {{/key}}

  • render once if key does not exist, is false, or is an empty list

Comments {{! some comment }}

Partials {{> key }}

  • rendered at run time
  • recursive partials are possible
  • use as includes/imports/nests
  • to include "key.hbs" into the place (handlebars)
  • not relative path, everytime use "path1/path2/file" to refer absolute path /path1/path2/file.hbs (from class root, for example)
  • inherit context

Template inheritance

  • use the block / partial syntax
  • {{#partial "id" }}...{{/partial}} - define a partial
  • {{#block "id"}}....{{/block}} - define a block to be filled with a partial

{{> (partialNameHelper) }}

Set delimiter {{=<% %>=}} change {{ }} to <% %>

Handlebars

http://jknack.github.io/handlebars.java/

Handlebar has helpers to extend mustache, some built-ins are:

with {{#with story}} ... {{/with}} - switch context

each {{#each a_collection }} ... {{/each}} / can do with plain mustache {{#

if {{#if active}} ...{{else}} ... {{/if}} / or plain mustache

unless {{#unless active}}...{{/unless}} / can do with plain mustache

Helpers:

See http://jknack.github.io/handlebars.java/helpers.html

Basic helpers {{name context? [argument]* [hash]*}}

Block helpers {{#name context? [argument]* [hash]* }} ... {{/name}}

name context? [argument]* [hash]*}}

...

{{/name}}

Name, context, argument, and hash are defined as follows:

name: qid;
context: expression;
argument: expression;
hash: id '=' expression;
expression: literal | qid;
qid
  :
     '../' qid
  |  id '[' .* ']' ('.' | '/') qid
  |  id '[' .* ']' qid
  |  id ('.' | '/') qid
  |  'id'
  ;
id: idStart (idStart | idPart)*;
idStart: [a-zA-Z_$@];
idPart: [0-9];
literal
  :
    stringLiteral
  | boolLiteral
  | numberLiteral
  | 'this'
  | '.'
  ;
stringLiteral:'"' .* '"';
boolLiteral:'true' | 'false';
numberLiteral: '0'..'9'+;

To register custom helpers

Register one at a time:

handlebars.registerHelper("blog", new Helper<Blog>() {
  public CharSequence apply(Blog blog, Options options) {
    return options.fn(blog);
  }
});

To register multiple helpers at once, register a

Helper Source

  • a class
  • public method (registered as a helper)
    • returning a CharSequence
    • can be static
    • name -> name of helper
    • Context, parameters and options are all optionals
    • If context is present it must be the first method argument
    • If options is present it must be the last method argument

Example:

public 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 ...
  }
}

Register: an instance, or xx.class

Helper options:

Parameter with position, with method argument, with auto boxing:

{{blog this "arg0"}}
...
public CharSequence apply(Blog blog, Options options) {
  return options.param(0);
}
{{blog this "arg0"}}
...
public CharSequence blog(Blog blog, String arg0) {
  return arg0;
}
{{blog this "string" true 678}}
...
public CharSequence blog(Blog blog, Options options) {
  String str = options.param(0);
  boolean bool = options.param(1);
  int num = options.param(2);
}
{{blog this "string" true 678}}
...
public CharSequence blog(Blog blog, String str, boolean bool, int num) {
}

Access a context stack value

{{blog this author}}
...
public CharSequence blog(Blog blog, Author author) {
  return author.getName();
}

handlebars.registerHelper(Handlebars.HELPER_MISSING

Registers a default helper

Loader

http://jknack.github.io/handlebars.java/loader.html

Able to load templates from file system, class path, web context and more

APIs: (no need to implement unless using non-default source)

  • TemplateLoader
    • Before name of template being solved, prefix & suffix gets added.
      • String DEFAULT_PREFIX = "/"; (depends on specific template loader implementation)
      • DEFAULT_SUFFIX = ".hbs"; (default .hbs but can be changed)
    • E.g. "hello" with prefix=/site suffix=.html -> /site/hello.html
    • Exactly same rules for partials {{> hello }}
    • This is done by TemplateLoader
  • TemplateSource

ClassPathTemplateLoader

  • default loader if none is set
  • default prefix: / suffix .hbs
  • usage: handlebars.compile("hello");
  • usage with specific prefix/suffix: see doc

FileTemplateLoader: see doc

ServletContextTemplateLoader: see doc

CompositeTemplateLoader: combine two or more loaders

Context Stack

Important concept. com.github.jknack.handlebars.Context object represents the context stack.

Resolution algorithm looks like: (first try current context, if not found, try parent, until resolved or resolved to null and nothing rendered)

  while (context != null && context.get("value") == null) {
    context = context.parent;
  }

Extending Context Stack

  • Combine: Context context = Context.newBuilder(first_for_example_a_model_hash).combine("user", second_for_example_user).build(); template.apply(context)
  • Value Resolver
    • by default, only maps & java bean objects are supported (registered)
    • FieldValueResolver.INSTANCE : resolve to private/public fields
    • MethodValueResolver.INSTANCE : access to public methods
    • DIY resolver - create your own
    • resolvers can be combined

Typesafe Template

See doc