Overview
Commands are instructions you give the compiler to create templates and add
custom logic to templates. Put commands within Closure Template tags, which are
delimited by braces ({}
). Specify the command name as the first token in the
tag (except the print
command, which is implied if no command is specified),
and add additional tokens to form the command text. For example, to create a
template, use the {template}
tag. Within the template, you can write other
commands to evaluate conditional expressions, iterate over data objects, or
print messages.
A few tags below belong at the top of a Closure Template file, while most of them belong within a template. Within the body of a template, any text within Closure Template tags is parsed and understood by the compiler, while anything outside of Closure Template tags is raw text that is output as-is. The syntax for commands depends on the specific command. This chapter describes every command.
delpackage
Syntax:
{delpackage <delegate_package_name>}
This command belongs at the start of a template file, before the namespace
tag. It must appear at the start of its own line. It is one of the two ways to
use delegate templates. For details, see the section on using delegate
templates with
delpackage.
namespace
Syntax (basic form):
{namespace <namespace>}
With all optional attributes:
{namespace <namespace> stricthtml="true"}
This command is required at the start of every template file. It must appear at
the start of its own line. It declares the namespace for the file, which serves
as the common prefix for the full name of every template in the file. You can
optionally declare strict autoescaping for every template in the file. You can
override this default in the template
tag.
Closure Templates allows multiple files to have the same namespace. However, Closure Library and Closure Compiler do not allow it, so if you're using Closure Templates in JavaScript together with other Closure technologies, avoid having the same namespace in multiple files.
alias
Syntax:
{alias <namespace>}
{alias <namespace> as <identifier>}
This command belongs at the start of the file, after the namespace
tag. It
must appear at the start of its own line. You can include any number of alias
declarations. Each declaration aliases the given namespace to an identifier. For
the first syntax form above, the alias is the last identifier in the namespace.
For the second syntax form, the alias is the identifier you specify. When you
call
a template in an aliased namespace, you don't need to type the whole
namespace, only the alias plus the template's partial name.
For example, if you declare
{alias long.namespace.root.projectx.mymodule.myfeature}
{alias long.namespace.root.projectx.foomodule.utils as fooUtils}
then you can replace the calls
{call long.namespace.root.projectx.mymodule.myfeature.myTemplate /}
{call long.namespace.root.projectx.foomodule.utils.someHelper /}
with
{call myfeature.myTemplate /}
{call fooUtils.someHelper /}
Delegate templates have their own full names (not a partial name prefixed by the
namespace), so alias
does not affect delegate calls (delcall
).
template
Syntax (basic form):
{template <template_name>}
...
{/template}
With all optional attributes:
{template <template_name> private="<true/false>" kind="html" stricthtml="true"}
...
{/template}
This command must appear at the start of its own line.
The template name must be a dot followed by an identifier because it is a
partial name relative to the file's namespace. For example, if the file's
namespace is ui.settings.login
and the template's partial name is
.newPassword
, then the full template name is ui.settings.login.newPassword
.
These are the template
tag's attributes:
private
: Optional. Defaultfalse
. Set this totrue
if you want this to be a private template. A private template can be called by other templates, including templates in other files, as long as the callers are all within the bundle of files that is compiled together. When you setprivate
totrue
, the compiler can optimize calls to that template in various ways, including inlining all calls of that template and removing that template altogether.autoescape
: Optional. Affects the encoding ofprint
commands and is set according to these rules:- If there is an
autoescape
value specified in thetemplate
tag, use that value. - Else if there is an
autoescape
value specified in the file'snamespace
declaration, use that value. - Else default to strict autoescaping.
- If there is an
kind
: Optional. Defaulthtml
. See the security guide for other content kinds.stricthtml
: Optional. Defaulttrue
. Enables checks for HTML validity of HTML templates with strict autoescaping.
@param
Syntax:
{template <template_name>}
{@param <param_name>: <param_type>}
{@param? <param_name>: <param_type>}
...
{/template}
{@param}
declares a template parameter. Parameter declarations must come first
in the template, preceding all other template content.
The first syntax form above (@param
) declares a required parameter that must
be provided whenever you call the template. The second form (@param?
) declares
an optional parameter, which does not always have to be provided. Within a
template, an optional parameter declared as {@param? <param_name>:
<param_type>}
has a type of <param_type>|null
.
Doc comments for params
Doc comments for parameters may appear before or after the parameter declaration. If before, the comment must be on the preceding line; if after, the comment must start on the same line as the parameter declaration.
Example:
{template .example}
/** A leading doc comment. */
{@param name: string}
{@param? height: int} /** A trailing doc comment. */
...
{/template}
Parameter type expressions
These are the type expressions that can appear in a parameter declaration.
Type Expression | Meaning |
---|---|
any |
The supertype of all types. Any value may be assigned to the any type, but the only operations that are allowed on the any type are printing and (implicit) casting to boolean. |
null |
The null type. Often used as a member of a union type to indicate the value might null or optional. |
? |
The unknown type. This type disables all type checking in expressions where it appears. All operations are allowed on values of the unknown type. |
string , bool , int , float , number |
Primitive types. (Note: number = int│float ) |
html , uri , js , css , attributes |
Sanitized content types (produced by templates using strict autoescaping).These represent pre-escaped strings for various contexts. |
a.b.c. , proto.foo.bar |
Object of specific named type. These types are not defined by Closure Templates. They must be defined externally and imported into the compiler's type registry via a Java plugin (not covered in |
list<ElementType> |
List containing elements of type ElementType. |
map<KeyType,ValueType> |
Map containing keys of type KeyType and values of type ValueType. Allows item access, for example {$a[b]} . |
[a:int, b:float] |
Record type. Allows field access, for example {$a.b}. |
type│type2 |
Union type. An a│b union may contain either a value of type a or a value of type b. |
Special Characters
Use the following special character commands to add raw text characters:
Special Character | Equivalent Raw Text |
---|---|
{sp} |
space |
{nil} |
empty string |
{\r} |
carriage return |
{\n} |
new line (line feed) |
{\t} |
tab |
{lb} |
left brace |
{rb} |
right brace |
Follow these heuristics when deciding when to use {sp}
and {nil}
:
{sp}
: Use{sp}
to insert a space character at the beginning or end of a line, in situations where the line-joining algorithm would not normally insert a space (specifically, at line-joining locations bordered by an HTML or template tag on either side).{nil}
: If a line joining location normally adds a space, but you want to make the lines join without a space, then add{nil}
at the end of the first line (most common) or the beginning of the second line. While the{nil}
itself turns into an empty string, it adds a template tag at the line-joining location, which eliminates the default single space.
literal
Syntax:
{literal} ... {/literal}
This command allows you to include a literal block of raw text, possibly
including special characters like braces. The rendered output is exactly what
appears in the literal
block. Note that this means that absolutely no
processing happens within a literal
block: no HTML escaping, no line joining,
no removal of indentation, no parsing for template commands, and no parsing for
comments.
Syntax (without directives):
{<expression>}
{print <expression>}
With directives:
{<expression> |<directive1> |<directive2> |...}
{print <expression> |<directive1> |<directive2> |...}
The print
command is the most common command in Closure Templates, so it
merits a short syntax in which you can omit the command name print
. When
Closure Templates encounters a print
tag, it simply inserts the result of the
expression, coerced to a string if necessary, into the rendered output.
NOTE: The command name `print` is optional.
Print directives
Print directives are post-processing on the output of a print
command. For a
list of print directives, see the Print
Directives chapter.
msg
Syntax (basic form):
{msg desc="<help_text_for_translators>"}
...
{/msg}
With all optional attributes:
{msg meaning="<differentiator>" desc="<help_text_for_translators>"}
...
{/msg}
Use the msg
tag to mark a section of the template as a message that needs to
be translated. The required desc
attribute provides explanation text to the
(human) translators.
NOTE: Do not break up a sentence or phrase into multiple messages. Translation is most effective with the context of the entire sentence or phrase.
Within a msg
block, the compiler turns HTML tags and Closure Template tags
into placeholders, with the exception of template tags that generate raw text
(e.g. special character commands and literal
blocks), which are simply
substituted. Messages that contain placeholders might be confusing to
translators, so you should consider providing (in the desc
attribute) an
example of how the message looks to the user.
Here are details on how Closure Templates generate placeholders in messages:
The compiler uses fixed names for common HTML tags, e.g.
<a ...>
becomes{START_LINK}
and<br />
becomes{BREAK}
.A simple
print
tag that prints a data reference turns into a placeholder that's named according to the last key name, by default. For example,{$user.emailAddress}
turns into the placeholder{EMAIL_ADDRESS}
.A complete
call
block (up to the closing/call
) turns into a single placeholder. The placeholder is named{XXX}
by default, so if you put acall
inside amsg
, be sure to provide a clear example of what the message might look like in thedesc
attribute.If you find a need to specify a placeholder name for a
print
orcall
instead of allowing the compiler generate the default name, you can add thephname
attribute. The value can either be camel case or upper underscore. For example,{call .emailAddr phname="emailAddress" /}
causes the placeholder name to beEMAIL_ADDRESS
instead ofXXX
. Note that changing a placeholder name changes the message id, causing retranslation.If a message contains multiple placeholders that would result in the same placeholder name, then the compiler appends number suffixes to distinguish them, e.g.
START_LINK_1
,START_LINK_2
.Template commands for control flow (
if
,switch
,foreach
,for
) are not allowed inmsg
blocks. However, you can put them in alet
block and then print the resulting reference from a within amsg
block. If you do this, make sure thelet
block has absolutely no translatable content.
Example message:
{msg desc="Says hello and tells user to click a link."}
Hello {$userName}! Please click <a href="{$url}">here</a>.
{/msg}
Example of what the translators see:
Hello {USER_NAME}! Please click {START_LINK}here{END_LINK}.
Example translation that one translator might give us:
{START_LINK}Zhere{END_LINK} zclick zplease. {USER_NAME} zhello!
Example output (for userName = 'Jess'
and url = 'http://www.google.com/'
):
<a href="http://www.google.com/">Zhere</a> zclick zplease. Jess zhello!
Use the meaning
attribute when you have two messages that are exactly the same
string in English, but might be translated to different strings in other
languages. The meaning
should then be a short string that distinguishes the
two messages, and is used for generating different message ids. Translators do
not see the meaning
attribute, so you must still communicate all the details
in the desc
. For example:
{msg meaning="noun" desc="The word 'Archive' used as a noun, i.e. an information store."}
Archive
{/msg}
{msg meaning="verb" desc="The word 'Archive' used as a verb, i.e. to store information."}
Archive
{/msg}
See more information about translating messages in the Translation chapter.
fallbackmsg
Syntax:
{msg <new_attributes>}
<new_message>
{fallbackmsg <old_attributes>}
<old_message>
{/msg}
If you want to change a message in-place, without worrying about accidentally
displaying the untranslated new source message to foreign-language users while
the new message is getting translated, use the fallbackmsg
feature. The
compiler automatically chooses the appropriate version of the message to use. If
the new message's translation is in the message bundle, then it is used, else
the compiler falls back to using the old message's translation. (This decision
happens at message insertion time or render time in JavaScript or Java usage,
respectively.)
When changing a message, move the old message attributes and content to the
fallbackmsg
portion. Then, write the new message attributes and content in the
msg
portion.
For example, to fix a typo in this message:
{msg meaning="verb" desc="button label"}
Arcive
{/msg}
replace it with the new message:
{msg meaning="verb" desc="button label"}
Archive
{fallbackmsg meaning="verb" desc="button label"}
Arcive
{/msg}
When replacing an existing message, you can change the message structure, meaning, placeholders, etc. There are no technical limitations on the relationship between the old and new messages.
Periodically, you might want to clean up your code by grepping for old
fallbackmsg
s and removing them. Although, it's not particularly harmful to
leave fallbackmsg
s in the code longer than necessary.
let
Syntax for arbitrary values:
{let $<identifier>: <expression> /}
Syntax for rendering to string ("block form"):
{let $<identifier>}...{/let}
let
defines a name for an intermediate value. The name is defined only within
the immediate code block containing the let
command, and the value of the name
is not modifiable. You might use let
because you need to reuse the
intermediate value multiple times, or you need to print a rendered value using a
directive, or you feel it improves the readability of your code.
Example:
{let $isEnabled: $isAaa and not $isBbb and $ccc == $ddd + $eee /}
{if $isEnabled and $isXxx}
...
{elseif not $isEnabled and $isYyy}
...
{/if}
The second syntax form listed above renders the contents of the let
block to a
string, including applying autoescaping. It is sometimes needed, but should be
used sparingly.
if, elseif, else
Syntax:
{if <expression>}
...
{elseif <expression>}
...
{else}
...
{/if}
Use the if
command for conditional output. The compiler evaluates the
expressions for if
and optional elseif
clauses in a boolean context. As soon
one expression evaluates to true, the compiler processes that sub-block and
skips the rest of the if
statement. Any elseif
clauses that appear after the
one that evaluates to true are never evaluated. The compiler only processes the
optional else
command when none of the if
and elseif
conditions are true.
For example:
{if round($pi, 2) == 3.14}
{$pi} is a good approximation of pi.
{elseif round($pi) == 3}
{$pi} is a bad approximation of pi.
{else}
{$pi} is nowhere near the value of pi.
{/if}
Example output (for pi = 2.71828):
2.71828 is a bad approximation of pi.
switch, case, default
Syntax:
{switch <expression>}
{case <expression_list>}
...
{case <expression_list>}
...
{default}
...
{/switch}
Use the switch
command for conditional output based on the value of an
expression. Each case
can have one or more expressions (use a comma-separated
list if you have more than one), and the case
matches when any one of its
expressions matches the value of the switch
expression. When a case
matches,
the compiler process that sub-block and the skips the remainder of the switch
statement. The compiler processes the optional {default}
command if none of
the expressions from the case
statements match the switch
expression.
For example:
{switch $numMarbles}
{case 0}
You have no marbles.
{case 1, 2, 3}
You have a normal number of marbles.
{default} // 4 or more
You have more marbles than you know what to do with.
{/switch}
Example output (for numMarbles
= 2):
You have a normal number of marbles.
foreach, ifempty
Syntax:
{foreach <local_var> in <data_ref>}
...
{ifempty}
...
{/foreach}
The foreach
command iterates over a list. The iterator local_var
is a local
variable that is defined only in the block. Within the block, you can use three
special functions that only take the iterator as their argument:
isFirst($var)
returnstrue
only on the first iteration.isLast($var)
returnstrue
only on the last iteration.index($var)
returns the current index in the list. List indices are 0-based.
The optional ifempty
command is for a fallback section that is executed when
the list is empty.
For example:
{foreach $operand in $operands}
{if not isFirst($operand)} + {/if}
{$operand}
{ifempty}
0
{/foreach}
Example output (for operands = ['alpha', 'beta', 'gamma']
):
alpha + beta + gamma
for
Syntax:
{for <local_var> in range(<expression1>, <expression2>, <expression3>)}
...
{/for}
The for
command generates a simple numerical loop. The iterator local_var
is
a local variable that is defined only in the block. The range()
function can
take one, two, or three arguments. With 3 arguments, the first is the initial
value, the second is the limit (note that the loop only continues if the loop
variable's value is strictly less than the limit), and the third is the
increment. With two arguments, the increment is assumed to be 1. With only one
argument, the initial value is assumed to be 0. For example:
range(10) → 0 1 2 3 4 5 6 7 8 9
range(4, 10) → 4 5 6 7 8 9
range(4, 10, 2) → 4 6 8
For example:
{for $i in range($numLines)}
Line {$i + 1} of {$numLines}.<br>
{/for}
Example output (for numLines = 3
):
Line 1 of 3.<br>Line 2 of 3.<br>Line 3 of 3.<br>
call, param
The call
command calls a template (the callee) and inserts its output into
the current template (the caller).
To provide the parameters to the callee template, you can pass values using
param
commands, pass all or part of the caller template's data
, construct
values to pass using param
commands, or use a combination of these. The
following sections discuss these five options in turn, using this example callee
template:
{template .exampleCallee}
{@param largerNum: int} /** An integer larger than 'smallerNum'. */
{@param smallerNum: int} /** An integer smaller than 'largerNum'. */
{$largerNum} is greater than {$smallerNum}.
{/template}
However, you can omit the param
values and the data
attribute if there are
no parameters to be passed.
{call .exampleCalleeWhichTakesNoParameters /}
Pass values using param
commands (recommended)
To pass values to the callee, the param
commands inside the call
function
must match the names of the callee's parameters.
{template .exampleCaller}
{let $largerNum: 20 /}
{let $smallerNum: 10 /}
{call .exampleCallee}
{param largerNum: $largerNum /}
{param smallerNum: $smallerNum /}
{/call}
{/template}
In another example, if there is a list pairs
where each element is a map
containing keys largerNum
, and smallerNum
, then you can use the following:
{template .exampleCaller}
...
{foreach $pair in $pairs}
{call .exampleCallee}
{param largerNum: $pair.largerNum /}
{param smallerNum: $pair.smallerNum /}
{/call}
{/foreach}
{/template}
This is the recommended way of calling templates in Soy as explicit data passing makes code more readable and easier to debug.
Pass part of the caller's data
Another way to pass data to the callee, is to use param
commands inside the
call
. For this to work, the param
commands inside the call
function must
match the names of the callee's parameters.
For example, if the caller's data includes a list pairs
where each element is
a map containing keys largerNum
, and smallerNum
, then you can use
data="$pair"
in the following example:
{template .exampleCaller}
...
{foreach $pair in $pairs}
{call .exampleCallee data="$pair" /}
{/foreach}
{/template}
Pass all of the caller's data
Alternatively, you can use the special attribute data="all"
to pass all of the
caller's data. This means that the callee's parameters must be a subset of the
caller's data, and have the same names. In such cases, you can use data="all"
instead of re-listing the parameters. For example, if the caller's data contains
two parameters aaa
and bbb
, and you want to pass both to the callee (with
the same parameter names), you can simply set data="all"
.
// Discouraged.
{template .exampleCaller}
{call .exampleCallee data="all" /}
{/template}
While this seems advantageous, it has considerable drawbacks for code maintenance, as parameters could potentially be passed through several call layers before they're used and obfuscate where a value is coming from. Take the following example:
{template originalCallee}
{@param requiredNum: int}
{@param? optionalNum: int}
{call passthroughCallee1 data="all" /}
{/template}
{template passthroughCallee1}
{@param requiredNum: int}
{call passthroughCallee2 data="all" /}
{/template}
{template passthroughCallee2}
{@param requiredNum: int}
{call finalCallee data="all" /}
{/template}
{template finalCallee}
{@param requiredNum: int}
{@param? optionalNum: int}
{/template}
If you were debugging why optionalNum
is being passed to finalCallee
when
called through originalCallee
, it may not be straightforward to determine that
it's been "passed through" on two levels of calls implicitly.
This is a fairly simple example where the templates are short and take few
parameters, however you can see this becoming much harder to traceback in
real-world usage. Therefore, the aforementioned explicit usage of param
enclosed parameters is considered the best practice, and usage of data=all
is
discouraged.
Construct values to pass
The param
command constructs values to pass to the callee.
Syntax for arbitrary values:
{param <key>: <expression> /}
Syntax for rendering to string ("block form"):
{param <key>}...{/param}
The first form is for arbitrary values and should be used in most cases.
The second form renders the contents of the param
block to a string, including
applying autoescaping (based on the specified content kind
). It is needed in
some situations, but it's usually an error to use it when the first form would
suffice. A common heuristic is that if you're using the second form with a
param
block that contains a single print
tag:
{param aaa}{$bbb}{/param}
then it's usually an error, and should be replaced with:
{param aaa: $bbb /}
Back to our example, assume that the caller's data includes a list pairs
where
each element is a map containing key largerInt
(note the name change) and only
sometimes contains the key smallerInt
(otherwise undefined). Use this to make
the call:
{foreach $pair in $pairs}
{call .exampleCallee}
{param largerNum: $pair.largerInt /}
{param smallerNum}
{if $pair.smallerInt}
{$pair.smallerInt} // if defined, pass it
{else}
{$pair.largerInt - 100}
{/if}
{/param}
{/call}
{/foreach}
The above example demonstrates the two forms of the param
command. The first
form evaluates an expression and passes the result as the parameter. The second
form renders an arbitrary section of Soy code and passes the result as the
parameter.
If you're observant, you've probably noticed two issues. The first issue is the
value passed for smallerNum
is now a string, not an integer (fortunately in
this case the callee only uses the parameter in a string context). If you
noticed this issue, then perhaps you also noticed that the computation for
smallerNum
can be put into an expression so that it can be passed as an
integer:
{param smallerNum: $pair.smallerInt ? $pair.smallerInt : $pair.largerInt - 100 /}
The second issue is if $pair.smallerInt == 0
, then it would be falsy and so
$largerInt - 100
would be passed even though $pair.smallerInt
is defined and
should be passed. This is why you should generally avoid using sometimes-defined
map keys. If you do use them, consider the possibility of falsy values. In this
example, if there's a possibility that $pair.smallerInt == 0
, you could
correct the bug by rewriting the expression using the function isNonnull
:
{param smallerNum: isNonnull($pair.smallerInt) ? $pair.smallerInt : $pair.largerInt - 100 /}
or better yet, use the null-coalescing operator:
{param smallerNum: $pair.smallerInt ?: $pair.largerInt - 100 /}
Use a combination of these
Finally, you can mix the two options above: you can pass some of the callee's
parameters directly from the caller's data, and pass the rest of the parameters
using param
commands. For example, if the caller's data includes an integer
largerNum
and a list of integers smallerNums
that are all smaller than
largerNum
, you could use the following template:
{foreach $smallerNum in $smallerNums}
{call .exampleCallee data="all"}
{param smallerNum: $smallerNum /}
{/call}
{/foreach}
Here, data="all"
passes in the callee's original template data largerNum
and
the list smallerNums
. However, because data="all"
does not pass in the
variable smallerNum
, you must pass it explicitly using the param
command.
NOTE: The `data` attribute of a `call` tag can only be used to pass all or part of the callee's original template data.
Calling a template in a different namespace
All of the above examples demonstrate calling a template in the same namespace,
hence the partial template name (beginning with a dot) used in the call
command text. To call a template from a different namespace, use the full
template name including namespace. For example:
{call myproject.mymodule.myfeature.exampleCallee}
{param pair: $pair /}
{/call}
Or, if you've aliased the namespace of the template you're calling to its last identifier, then
{call myfeature.exampleCallee}
{param pair: $pair /}
{/call}
Delegate templates (with delpackage)
Delegate templates allow you to write multiple implementations of a template and
choose one of them at render time. Delegate templates are defined and called
using deltemplate
and delcall
, which have syntax similar to template
and
call
.
There are two independent ways to use delegates, differing in how you control
which delegate implementation is called. This section explains how to use
delegates with delegate packages (delpackage
), which is appropriate for cases
where you don't intend to send code for unused delegate implementations to the
client, (for example, an experiment whose code is only sent to a small subset of
users.) The next
section explains how
to use delegates with the variant
attribute, which is appropriate for finer
control of delegate selection at the call site.
main.soy
syntax:
{namespace ...}
/** Caller (basic template, not delegate template). */
{template ...}
{delcall aaa.bbb.myButton allowemptydefault="false" data="..." /}
{/template}
/** Default implementation. */
{deltemplate aaa.bbb.myButton}
...
{/deltemplate}
experiment.soy
syntax:
{delpackage MyExperiment}
{namespace ...}
/** My experiment's implementation. */
{deltemplate aaa.bbb.myButton}
...
{/deltemplate}
The implementations must appear in different files, and each file other than the
default implementation must declare a delegate package name (delpackage
). This
is the identifier used to select the implementation at usage time.
The delegate template names are not within the file's namespace; namespaces only apply to basic templates. Instead, delegate template names are just strings that are always written in full. They can be any identifier or multiple identifiers connected with dots.
Template files can have an optional delpackage
declaration at the top, just
above the namespace
declaration. And multiple files can have the same
delpackage
name, putting them all within the same delegate package. If a
delegate template is defined in a file without delpackage
, then it is a
default implementation. If a delegate template is defined in a file with
delpackage
, then it is a non-default implementation.
At render time, when a delegate call needs to be resolved, Closure Templates looks at all the "active" implementations of the delegate template and uses the implementation with the highest priority. Basically, this means:
- Use a non-default implementation, if there is one.
- Otherwise, use the default implementation, if there is one.
- Otherwise, if the
delcall
has the attributeallowemptydefault="true"
, then the call renders to the empty string. - Otherwise, an error occurs.
Due to the third case, it is legal to render a delegate template that has no
default implementation, as long as the delcall
has allowemptydefault="true"
.
What counts as an "active" implementation depends on the backend in use. In
JavaScript, an active implementation is simply an implementation that is defined
in the JavaScript files that are loaded. Ship only the generated JavaScript
files for the active delpackage
s.
In Java, use SoyTofu.Renderer#setActiveDelegatePackageSelector
to set the
active implementations. For example, with the example template code above, call
myTofu.newRenderer(...)
.setActiveDelegatePackageSelector(Predicates.equalTo("MyExperiment"))
.setData(...)
.render()
This will use the non-default implementation of aaa.bbb.myButton
from the
MyExperiment
delegate package. On the other hand, if you omit the call to
setActiveDelegatePackageSelector()
, or if you pass a set not including
MyExperiment
, it will use the default implementation of aaa.bbb.myButton
.
In either backend, it is an error to have more than one active implementation at the same priority (for example, multiple active non-default implementations).
Delegate Templates (with variant)
Delegate templates allow you to write multiple implementations of a template and
choose one of them at render time. Delegate templates are defined and called
using deltemplate
and delcall
, which have syntax similar to template
and
call
.
There are two independent ways to use delegates, differing in how you control
which delegate implementation is called. The previous
section explains
how to use delegates with delegate packages (delpackage
), which is appropriate
for cases where you don't intend to send code for unused delegate
implementations to the client, (for example, an experiment whose code is only
sent to a small subset of users). This section explains how to use delegates
with the variant
attribute, which is appropriate for finer control of delegate
selection at the call site.
Syntax:
/** Caller (basic template, not delegate template). */
{template ...}
{delcall aaa.bbb.myButton variant="$variantToUse" /}
{/template}
/** Implementation 'alpha'. */
{deltemplate aaa.bbb.myButton variant="'alpha'"}
...
{/deltemplate}
/** Implementation 'beta'. */
{deltemplate aaa.bbb.myButton variant="'beta'"}
...
{/deltemplate}
The delegate template names are not within the file's namespace; namespaces only apply to basic templates. Instead, delegate template names are just strings that are always written in full. They can be any identifier or multiple identifiers connected with dots.
The variant in a deltemplate
command must be a string literal containing an
identifier. If no variant is specified, then it defaults to the empty string.
The variant in a delcall
command can be an arbitrary expression, as long as it
evaluates to a string at render time.
At render time, when a delegate call needs to be resolved,
- Use the delegate implementation with matching variant, if there is one.
- Otherwise, use the delegate implementation with no variant, if there is one.
- Otherwise, an error occurs.
css
Syntax:
{css <command_text>}
The css
command is a layer of indirection that frees you from having to
hard-code CSS class names in your template files. The behavior depends on the
backend in use. In JavaScript, {css 'foo'}
becomes a call to the Closure
Library function goog.getCssName('foo').
. In Java, {css 'foo'}
becomes a
lookup into a SoyCssRenamingMap
, which can be set with
Renderer#setCssRenamingMap()
.
xid
Syntax:
{xid <command_text>}
The xid
command is useful for minification and obfuscation of strings. In
JavaScript, it is replaced by a call of xid
function. This function is not
defined by Soy; users need to define it themselves. In Java, users can specify a
renaming map using SoyTofu#setIdRenamingMap
. If this map is not specified then
the default implementation just appends _
to the passed string.