Templates

Introduction

Fiz templates provide a convenient mechanism for combining static and dynamic data to generate HTML, Javascript code, and URLs; they are typically used in Java code that renders sections and other components. For example, consider the following code, which generates an HTML <table> tag using some computed values:

Template.expandHtml("<table id=\"@id\" {{class=\"@class\"}}>",
        new Dataset("id", "cart", "class", "ShoppingCart"));

The first argument to Template.expandHtml is a template. A method returns a new string formed by expanding the template:

  • The substring @id is replaced with the value of the entry named id in the Dataset passed as second argument.
  • If the dataset contains an entry named class (which it does in this case) then the value of that entry replaces the string @class and the {{ and }} are dropped.

In this case the result is the following string:

<table id="cart" class="ShoppingCart">

If the dataset had not contained a class entry then all of the text from {{ through }} would have been skipped, producing the following result:

<table id="cart">

Templates allow you to write more compact and understandable Java code. They also enhance the security of your application, because they automatically escape special characters in substituted values (more on this below).

Substitutions

When a template is expanded, each character from the template is copied to the output string except for the following patterns, which cause substitutions:

@name

Copy the contents of the variable named name to the output. name consists of all the standard Unicode identifier characters following the @. An error is generated if the variable doesn't exist.

@(name)

Copy the contents of the variable named name to the output. name consists of all the characters following the ) up to the next ). @ can be used between the parentheses to perform substitutions on the name; for example, @(@foo) finds variable foo, uses its value as the name of another variable, and copies the value of that variable to the output. An error is generated if the variable(s) don't exist.

@name?{...}

Default: if name exists and has a nonzero length, copy its contents to the output and skip over ?{...}. Otherwise, perform template expansion on the text between the braces.

@name?{t1|t2}

Choice: if name exists and has a nonzero length then perform template expansion on t1. Otherwise perform template expansion on t2.

{...}

Conditional substitution: normally the information between the braces is processed just like the rest of the template, except that the braces are not copied to the output. However, if the information between the braces contains a reference to a variable that doesn't exist or has zero length then all of the information between the braces is skipped: nothing is copied to the output. Furthermore, if there are space characters next to either of the curly braces then one of the spaces may be removed to avoid redundant spaces (enclose the space in {} to keep this from happening).

@@

Append @ to the output.

@{

Append { to the output.

@}

Append } to the output.

@*

Any other occurrence of @ besides those described above is illegal and results in an error.

Selecting variables

The variables for template substitution can be named in two ways. If a name starts with an alphabetic character then it refers to an entry in a dataset passed into the expansion method:

Template.expandHtml("Name: @name, age: @age", new Dataset("name", "Alice", "age", 34"))

--> Name: Alice, age: 34

This form is typically used to include data from a section's configuration properties or the result of a data request. If the name is an integer then it refers to an explicit argument passed into Template.expand. @1 refers to the first argument, @2 refers to the second argument, and so on:

Template.expandHtml("Name: @2, age: @1", "34", "Alice")

--> Name: Alice, age: 34

These two forms can also be combined:

Template.expandHtml("Name: @name, age: @1", new Dataset("name", "Alice", "age", 34"),
        "25")

--> Name: Alice, age: 25

Escaping special characters

When substituting values in a template, Fiz automatically escapes special characters in the substituted values. This behavior is important both to avoid HTML errors and to prevent security loopholes that could result in cross-site scripting (XSS) attacks. Template.expandHtml assumes that you are generating HTML, so characters that have special meaning in HTML, such as < and >, are replaced with the corresponding HTML entities:

Template.expandHtml("<input value=\"@1\">", "<>")

--> <input value="&lt;&gt;">

Note that special characters in the template itself are not escaped; only characters in substituted values are escaped.

Templates support three different kinds of escaping (HTML, URLs, and Javascript) with different methods for each:

Template.expandHtml("<input value=\"@1\">", "abc<\">")

--> <input value="abc&lt;&quot;&gt;">
Template.expandJs("var x = \"%1\";", "abc<\">")

--> var x = "abc<\">";
Template.expandUrl("http://mycompany.com/a/b/c?value=@1", "abc<\">")

--> "http://mycompany.com/a/b/c?value=abc%3c%22%3e"

Each form of escaping handles a different set of special characters and escapes them in a different way. Template.expandJavascript assumes that you are substituting into a Javascript string, so it uses the Javascript conventions for quoting special characters in strings, and Template.expandUrl uses URL encoding. You can also use Template.expandRaw if you want values to be substituted with no escaping.

Sometimes you will need to combine multiple template expansions with different kinds of escaping in order to produce the desired result. For example, suppose you are trying to generate HTML for a <div> with a Javascript event handler that will store a particular URL in a Javascript variable, and that you want the URL to include a query value that has been computed dynamically. To do this you can use the following series of expansions:

companyName = "C&H Sugar";
...
String url = Template.expandUrl("/partners/validate?company=@1", companyName);
String javascript = Template.expandJs("form.url = \"@1\";", url);
String html = Template.expandHtml("<div onclick=\"@1\">", javascript);

Template.expandUrl escapes the ampersand and space characters in the company name to produce the following URL:

    /partners/validate?company=C%26H+Sugar

Template.expandJavascript substitutes the URL into a Javascript string. In this case it turns out that no additional escaping is necessary, but you should always check for escaping whenever substituting dynamic values (don't assume you know enough about the format of the value to skip the escaping):
    form.url = "/partners/validate?company=C%26H+Sugar";

Finally, Template.expandHtml includes the Javascript in an HTML attribute, escaping the quote characters in the Javascript code to produce the following result:
    <div onclick="form.url = &quot;/partners/validate?company=C%26H+Sugar&quot;;">

WARNING!! It is not safe to substitute different kinds of data in a single expansion, as in this incorrect example:

Template.expandHtml("<input onclick=\"showMessage('@1');\" value=\"@2\">", message, default);

In this example @1 substitutes message into a snippet of Javascript, while @2 substitutes default into an HTML attribute. Since expandHtml was invoked the @2 substitution will be properly escaped, but @1 will not. If expandJs were invoked instead, then @1 would be escaped properly but not @2. You must use a separate template expansion for each different kind of data, like this:

Template.expandHtml("<input onclick=\"@1\" value=\"@2\">", 
        Template.expandJs("showMessage('@1');", message),
        default);

Philosophy

Fiz uses templates in a different way than other frameworks: Fiz templates tend to be smaller, simpler, and used primarily in Java code.

Most other frameworks include a more powerful templating mechanism where arbitrary code can be embedded in templates and templates are stored in separate files. In these frameworks the templates provide the primary mechanism for creating Web pages: every Web page is described with one or more templates. This approach works for simple document-oriented Web pages but does not work as well for Web applications. Templates encourage developers to think about low-level HTML rather than the high-level structure of the application. Furthermore, Web pages for applications tend to be highly conditionalized, so the templates end up with too much code to visualize the HTML structure, and too much HTML to visualize the code structure.

Our philosophy for Fiz is that most of the functionality for creating Web pages should be encapsulated inside components. Developers should not write HTML to create Web pages; they should describe the pages with collections of components that generate the page. Thus, in Fiz the primary mechanism for describing Web pages is sections, not templates.

Nonetheless, templates provide a useful mechanism for generating HTML and Javascript inside components, just like printf provides a convenient mechanism for formatting output strings in C. In other words, templates are used primarily at a low level in Fiz, whereas in other frameworks they tend to be used at a high level.