Closure Tools

Closure Template Concepts

Table of Contents

  1. Compiler Backends
  2. Template Data
  3. File Structure
  4. Command Syntax
  5. Expressions
  6. Injected Data
  7. Comments
  8. Raw Text
  9. Line Joining

Compiler Backends

The Closure Templates compiler has one frontend for parsing template syntax and constructing an intermediate tree representation, and multiple backends for supporting usage from multiple programming languages. Currently, you can use one of two backends:

  • The JavaScript Source backend supports usage in JavaScript. This backend compiles each input template file into a corresponding output JavaScript file. Each non-private template maps to a corresponding JavaScript function of the same name.

  • The Java Tofu backend supports usage in Java. This backend compiles a bundle of template files into a Java object representation (called SoyTofu) that has a method to render any of the templates in the bundle.

Template Data

Closure Templates use a data model that's not specific to any language: you can use the same template in JavaScript and Java. The data that you pass into templates for processing is valid as long as it's structured as a tree with two types of internal nodes: a map or a list. All map keys must be identifiers (i.e. you can use letters, digits, and the underscore character). There are 5 types of leaf nodes (i.e. primitives): null, boolean, integer, float, and string. The root of a template data tree must be a map from parameter names to their values.

Each template compiler backend has its own representation for the data that's passed into templates. For example, consider a template that takes 2 parameters: the name of a person and the list of destinations that he or she recently traveled to.

In JavaScript, a valid data object is any JavaScript object (since all JavaScript objects are maps), for instance:

var data = {name: 'Melissa', destinations: ['Singapore', 'London', 'New York']};

In Java, a valid data object is either a SoyMapData instance or a Map<String, ?> (see the Java Usage chapter on reasons to use one or the other), for example:

SoyMapData data = new SoyMapData(
    "name", "Melissa",
    "destinations", new SoyListData("Singapore", "London", "New York"));
Map<String, Object> data = ImmutableMap.<String, Object>of(
    "name", "Melissa",
    "destinations", ImmutableList.of("Singapore", "London", "New York"));

For detailed info on each backend, refer to the JavaScript Usage and Java Usage chapters.

File Structure

Closure Templates appear in files with the extension .soy. These files are called Soy files. Each Soy file should contain a logical package of related templates.

All Soy files must be encoded in UTF-8. Note that ASCII is also valid because it's a proper subset of UTF-8.

Note: Windows users should note that the template compiler does not accept UTF-8 with a BOM (byte-order mark). Please save your Soy files as UTF-8 without any BOM.

Every Soy file must, at minimum, have the following components:

  • A namespace declaration.
  • One or more template definitions.

The Namespace Declaration

A Soy file must have exactly one namespace declaration, which must appear before the templates. A valid namespace is one or more identifiers joined with dots, for example:

{namespace examples.simple}

Note that for backends that generate source code (currently just the JavaScript Source backend), the namespace is used in the generated code. See the JavaScript Usage chapter for more details.

The namespace tag has an optional attribute for establishing the default autoescape mode for the templates in the file: {namespace examples.simple autoescape="contextual"}.

Template Definitions

Define one or more templates after the namespace declaration, using one template tag per template. You must create a unique name for each template and begin the template name with a dot, which indicates that the template name is relative to the file's namespace. Put the template's body between the template tag and the corresponding /template tag. Here is an example template:

/**
 * Says hello to a person.
 * @param name The name of the person to say hello to.
 */
{template .helloName}
  Hello {$name}!
{/template}

Precede each template with a SoyDoc comment that explains the purpose of the template, in the same style as JavaDoc. Add a @param declaration for each required parameter and a @param? declaration for each optional parameter of the template.

Note: You must put namespace, template, and /template commands, as well as the /** that starts a SoyDoc comment, at the start of their own lines.

Command Syntax

Commands are instructions that you give the template compiler to create templates and add custom logic to templates. Put commands within Closure Template tags, which are delimited by braces ({}). The first token within a tag is the command name (the print command is implied if no command is specified), and the rest of the text within the tag (if any) is referred to as the command text. Within the template you can enclose other commands to evaluate conditional expressions, iterate over data objects, or print messages.

The name of the command is the first token between the braces. The tag can end there or can contain command text in a format that is specific to the command. For some commands that mirror standard programming constructs, the command text is in a similar style. Here are three examples of commands:

{/foreach}
{if length($items) > 5}
{msg desc="Says hello to the user."}

If a command has a corresponding end command, then the end command's name is a / followed by the name of the start command, e.g. foreach and /foreach.

The most common command is print, which prints a value. You can write either {print $userName} or the short form {$userName}. Because print is so common, it is the implied command for all tags that don't explicitly begin with a command.

If you need to include brace characters within a template tag, use double braces to delimit the tag, e.g.

{{msg desc="Example: The set of prime numbers is {2, 3, 5, 7, 11, 13, ...}."}}

You can find explanations and example usages of all commands in Commands.

Expressions

You can write expressions within templates to reference template data, variables, or globals. Closure Templates use a language-neutral expression syntax. This section describes how to reference data, write literals for all the primitive types, and use basic operators.

Referencing Data

To reference template data, use a dollar sign followed by the parameter name, e.g. $userName. To reference deeper data, you can string multiple keys together using dots. For example, $folders.0.name evaluates to the value of the key name within the first element (index 0) of the folders list. However, this reference would only succeed if folders is a nonempty list of maps, and the maps contain the key name. Other equivalent ways to write this data reference are: $folders[0].name or even $folders[0]['name'].

The special prefix $ij is used for injected data, which is automatically injected into all subtemplates during a render. For example, $ij.foo references the injected data key foo.

Certain template constructs create local variables, which are referenced the same way as parameters from template data. Thus, the reference $user.email is correct whether user is a parameter or a local variable.

All of the above types of references can optionally access keys using ?. instead of . or using ?[…] instead of […]. These are called null-safe accesses. A null-safe access will evaluate to null when the left side (the portion before the ?) is either undefined or null, without attempting to continue evaluation. The two types of accesses (basic and null-safe) can be mixed. For example, the reference $aaa?.bbb.ccc?[0] is protected against $aaa being undefined or null, and is also protected against $aaa.bbb.ccc being undefined or null, but is not protected against $aaa.bbb being undefined or null.

A name without a preceding dollar sign is a global, e.g. app.css.CSS_NAV_LINK. A global is data that is not explicitly passed into the template as a parameter, but instead is expected to be provided some other way (e.g. either directly to the compiler during template compilation or defined in the environment in the case of JavaScript). Use globals judiciously, and reserve them for true global constants that should not sensibly be passed to the template as data. In JavaScript usage, globals can be bound either at compile time or at render time (or a combination of the two). In Java usage, there is no concept of render-time globals, so all globals must be bound at compile time.

Basic Types

This table describes how to write literals for primitive types, lists, and maps:

Type Literal
Null null
Boolean false or true
Integer Decimal (e.g. -827) or hexadecimal (must begin with 0x and must use capital A-F, e.g. 0x1A2B).
Float Must be in decimal and must either:
  • Have digits both before and after the decimal point (both can be a single 0), e.g. 0.5, -100.0, or
  • Have a lower-case e that represents scientific notation, e.g. -3e-3, 6.02e23.
Even though the primitive type is named Float, it has the precision of a number in JavaScript or a double in Java.
String Delimited by single quotes only. Supports these escape sequences: \\, \', \", \n, \r, \t, \b, \f, and \u#### where the #### can be any 4 hex digits to denote a Unicode code point.
List [<expr1>, <expr2>, ...]
For example: [1, 'two', [3, false]]
[] is the empty list.
Map [<keyExpr1>: <valueExpr1>, <keyExpr2>: <valueExpr2>, ...]
For example: ['aaa': 42, 'bbb': 'hello']
[:] is the empty map.
Note: Square brackets ([…]) delimit both lists and maps because braces ({…}) delimit commands.

Operators

Here are the supported operators, listed in decreasing order of precedence (highest precedence at the top):

  •   - (unary)   not
  •   *   /   %
  •   +   - (binary)
  •   <   >   <=   >=
  •   ==   !=
  •   and
  •   or
  •   ?: (binary)   ? : (ternary)

Note: Use parentheses to override precedence rules.

You can use the operator + for either adding numbers or concatenating strings. When one of the two operands is a string, the other value is coerced to a string. All primitive values have string representations. The string representation of a map or list is not well-defined, so don't print a map or list unless you're debugging.

The / operator performs floating point division. To divide two integers to get an integer, use the floor function or a similar function in tandem with the division operator.

Boolean operators are short-circuiting. When a non-boolean value is used in a boolean context, it is coerced to a boolean. Each primitive type has exactly one falsy value: null is falsy for null, false is falsy for booleans, 0 is falsy for integers, 0.0 is falsy for floats, and '' (empty string) is falsy for strings. All other primitive values are truthy. Maps and lists are always truthy even if they're empty. Undefined data keys are falsy. This allows you to check the presence of a data key before using it.

The binary operator ?: is called the null-coalescing operator (also known as the elvis operator). It evaluates to the left operand if the left operand is defined and not null (same check as the function isNonnull), else it evaluates to the right operand.

The ternary operator ? : evaluates the first operand as a boolean, and either evaluates to the second operand if the first operand is truthy, or evaluates to the third operand if the first operand is falsy.

Note: The checks done by the binary operator ?: and the ternary operator ? : are different. Specifically, $a ?: $b is not equivalent to $a ? $a : $b. Rather, the former expression is equivalent to isNonnull($a) ? $a : $b.

Functions

You can use some simple functions in Soy expressions. For a list of functions, refer to the chapter Functions and Print Directives.

Injected Data

Injected data is data that is available to every template. You don't need to use the @param declaration for injected data, and you don't need to manually pass it to called subtemplates.

Given the template:
{namespace ns autoescape="contextual"}

/** Example. */
{template .example}
  foo is {$ij.foo}
{/template}

In JavaScript, you can pass injected data via the third parameter.
// The output is 'foo is injected foo'.
output = ns.example(
    {},  // data
    null,  // optional output buffer
    {'foo': 'injected foo'})  // injected data
In Java, using the Tofu backend, you can inject data by using the setIjData method on the Renderer.
SoyMapData ijData = new SoyMapData();
ijData.put("foo", "injected foo");

SoyTofu tofu = ...;
String output = tofu.newRenderer("ns.example")
    .setIjData(ijData)
    .render();

Injected data is not scoped to a function like parameters. The templates below behave in the same way as the ".example" template above despite the lack of any data attribute on the call tag.
{namespace ns autoescape="contextual"}

/** Example. */
{template .example}
  {call .helper /}
{/template}

/** Helper. */
{template .helper private="true"}
  foo is {$ij.foo}
{/template}

Comments

Comments within templates follow the same syntax as Java or JavaScript, i.e.:

  • // begins a rest-of-line comment
  • /* comment */ delimit an arbitrary comment (can be multiline)

Note that // only begins a comment if the preceding character is whitespace. This is so that strings like URLs are not interpreted as comments.

Comments do not appear in the rendered output, but they also do not appear in the generated code for backends that generate source code (which is currently just the JavaScript Source backend). Note that HTML comments (i.e. <!-- ... -->) are not processed by the template compiler, and do appear in the rendered output.

Raw Text

Everything in a template that does not appear between braces is considered raw text, including all HTML tags. There are also a few special character commands and a literal command that generate raw text. Other than joining lines and removing indentation, the template compiler does not attempt to parse raw text, so the raw text simply appears in the rendered output exactly as it's written. The one exception is the text within a msg block, which you can learn about in the msg section of the Commands chapter.

Line Joining

Within the body of a template, you can indent the lines as much as you want because the template compiler removes all line terminators and whitespace at the beginning and end of lines, including spaces preceding a rest-of-line comment. The compiler completely removes empty lines that consist of only whitespace. Consecutive lines are joined according to the following heuristic: if the join location borders a template or HTML tag on either side, the lines are joined with no space. If the join location does not border a template or HTML tag on either side, the lines are joined with exactly one space.

The line joining heuristic is conservative in that it usually doesn't add a space where one is not wanted, but it sometimes doesn't add a space where one is needed. In the latter case, simply use the special character command {sp} to add the space you need. Note that in the rare former case, you can use {nil}.

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.