GUIDES Javascript Templates
1. Introduction
The UIZE JavaScript Framework implements a JavaScript templating system with rich template functionality that fully leverages the JavaScript language.
This templating system is implemented in the Uize.Template
module.
2. Similar to ASP and JSP
Developers with experience in asp
or jsp
syntax will find the UIZE JavaScript template syntax familiar and easy to understand.
The principle is very simple: stuff inside <%
and %>
delimiters is regarded as JavaScript code, and everything else is added to output.
EXAMPLE
<% for (var i = 0; i < 5; i++) { %>Hello World !
<% } %>
The above template would generate the following output, with five paragraphs containing the message Hello World!
...
OUTPUT
Hello World !
Hello World !
Hello World !
Hello World !
Hello World !
3. Benefits of Using JavaScript
Because the UIZE JavaScript Template system directly leverages JavaScript...
1. | parsing templates is very fast |
2. | the code required to parse templates is very small |
3. | you get the full power of the JavaScript language - not a hobbled templating language that is geared towards a lowest common denominator of implementation contexts |
4. | you don't have to learn a whole new language for writing your templating logic |
4. Compiled For Performance
JavaScript templates are parsed and "compiled" to JavaScript functions that can be called multiple times.
Because the parsing is only done the first time, repeat calls are substantially more efficient. A template string is compiled using the Uize.Template.compile
static method, as follows...
var helloWorldTemplate = Uize.Template.compile ( '<% for (var i = 0; i < 10; i++) { %>Hello World !
<% } %>' );
Once "compiled", the template can be executed simply by calling it as a function, as in...
var myTemplateOutput = helloWorldTemplate ();
5. Input Data
The function that is "compiled" from the template string expects a single parameter, that is known within the scope of your template's JavaScript code as input
.
Calling the compiled template function with a single argument passes this value to the input
variable in the function's local scope. Your template's JavaScript code can reference this parameter to qualify its execution. The value that you pass to your template function should be an object.
EXAMPLE
<% for (var i = 0; i < input.repeats; i++) { %>Hello World !
<% } %>
In the above JavaScript template, the template is expecting a repeats
property to be specified in the input
object when the template function is executed.
If you compiled the template as follows...
var helloWorldTemplate = Uize.Template.compile ( '<% for (var i = 0; i < input.repeats; i++) { %>Hello World !
<% } %>' );
...then you could call it as follows...
var myTemplateOutput = helloWorldTemplate ({repeats:10});
6. Assignment Expressions
The UIZE JavaScript Template syntax supports two types of assignment expressions: implicit input value references and full fledged JavaScript expressions.
6.1. Implicit Input Value References
It is common in many templating languages to have a simple way for substituting values into templates, using a token syntax.
The UIZE JavaScript Template syntax does not provide an additional dedicated token syntax for simple variable substitution, but instead provides a convenient shorthand for referencing the properties of the input
object and indicating that the value should be assigned to the template's output.
Basically, if the first character inside the <%%>
block is a period, then what follows is assumed to be the name of a property of the input
object. Consider the following example...
EXAMPLE
<% for (var i = 0; i < input.repeats; i++) { %><%.message%>
<% } %>
In the above example, the repeated message is now configurable through the message
property of the input
object. The <%.message%>
block sends its value to the template's output when the compiled template function is executed.
If you compiled the template as follows...
var messageRepeatedTemplate = Uize.Template.compile ( '<% for (var i = 0; i < input.repeats; i++) { %><%.message%>
<% } %>' );
...then you could call it as follows...
var myTemplateOutput = messageRepeatedTemplate ({message:'Hello World !',repeats:10});
6.2. Full Fledged JavaScript Expressions
In addition to the shorthand <%.myInputProperty%>
syntax, the UIZE JavaScript Template system also supports the traditional <%= %>
syntax.
Basically, if the first character inside the <%%>
block is an equal sign, then what follows is assumed to be a JavaScript expression whose value should be sent to the template's output when executed. Therefore, using the <%= %>
syntax it would be possible to also write the configurable message template shown earlier as follows...
<% for (var i = 0; i < input.repeats; i++) { %><%= input.message %>
<% } %>
The benefit of the <%= %>
syntax is that it allows for complete JavaScript expressions to be assigned to a template's output. Consider the following example...
EXAMPLE
<% for (var i = 0; i < input.repeats; i++) { %><%= i %> squared is <%= i * i %>
<% } %>
In the above example, a list of the squares of a series of integers of a specified length will be output in paragraphs. When you need to do anything more complex than substituting the value of an input in a particular spot in a template's output, then this syntax is the way to go. Also, because the <%. %>
syntax assumes the named identifier is a property of the input
object (that's what it's designed for), the <%= %>
syntax is the way to send the values of local variables to the template's output.
6.3. Encoding Output
It is not always desirable to send the value of an input property or local variable or expression to the template's output as is.
In some cases, one may be sending to output within a scope that requires specific encoding. For example, one may be placing a value as a query param value as part of a URL. In such a case, the value will need to be URI encoded. The UIZE JavaScript Template system provides a syntax for specifying an arbitrary series of encodings. Consider the following example...
EXAMPLE
<% .category %>
In the above example, the template generates a category search link. The link's text is simply the value of the category
property of the input
object, but when the category is placed inside the link tag's href
attribute as a query param, it needs to be URI encoded. This is done by using the urlPiece
encoding. If the above template were processed with the input
of {category:'Dogs and Cats'}
, it would produce the output...
Dogs and Cats
6.4. Other Encodings
There are other encodings as well.
Consider an example where all the search parameters are passed to a template in a searchParams
property, where the category is just one parameter. Then, the following template might do the trick...
<% .searchParams.category %>
If the above template were processed with the input
of {searchParams:{category:'Dogs and Cats',sort:'recent'}}
, it would produce the output...
Dogs and Cats
Some other examples of encodings include: json
, miniJson
, url
. Additional encodings can be registered with the Uize.Template
package by assigning new properties on the Uize.Template.encodings
object, or by calling the Uize.Template.defineStandardEncoding
static method. For more information on adding new encodings, consult the reference for the Uize.Template
module.
6.5. Chained Encodings
It is possible to chain an arbitrary number of encodings together. The syntax is simply -> encoding1 -> encoding2 -> ... -> encodingN
.
Say, for example, the search parameters for a URL were to be specified as a JSON object that was to be sent as a single "bucket" URL query param to the server. In such a case you could employ a template as follows...
<% .searchParams.category %>
In this example, the searchParams
property of input
would first be encoded to a compact JSON format and would then be subsequently URI encoded as a piece of a URL.
6.6. Encoding Options
Certain encodings may provide encoding options to let you qualify how the encoding should be performed.
An example is the json
encoding, which lets you specify indent characters, object key delimiter, quote character, etc. Such encoding options can be specified in JSON syntax right after the encoding name, as follows...
EXAMPLE
In the above example, the searchParams
property of input
is being written out into a script block, where the JSON encoding is being told to use a colon with spaces around it as the key delimiter for objects, thereby making the output more readable.
6.7. Reverse Encoding (i.e. Decoding)
A specific encoding can be reversed (provided that there is a defined decoder registered for that encoding) by simply prefixing the encoding name with the "!" character.
Let's say that the search params for a link generation template is being provided to the template as a serialized JSON string. If the server requires the search params as URL query params, then the template can first decode the JSON string to a query params object before encoding it as URL query params, as in the following example template...
EXAMPLE
<% .searchLinkText %>
6.8. Encoding In Other Contexts
The convenient encodings syntax only applies to <%. %>
and <%= %>
style assignment blocks.
If you wish to use encodings and/or decodings in more complex JavaScript code inside your templates, then you will have to use the Uize.Template.encode
and Uize.Template.decode
static methods. These methods provide access to all the same functionality, but in a more conventional (i.e. long-winded) manner.
For example, the following template...
<% .searchParams.category %>
...could also be written as...
<% .searchParams.category %>
For chained encodings, the encodings can be specified in the same way as with assignment expressions. For example, the following template...
<% .searchParams.category %>
...could also be written as...
<% .searchParams.category %>
7. Directives
Directives provide a way to guide how a template is compiled. They can be executed within templates by using the "@" prefix, as follows...
<%@ startBlock ('contents') %> ...my block of stuff... ...my block of stuff... ...my block of stuff... <%@ endBlock () %>
7.1. Block Directives
The @startBlock
and @endBlock
directives let you define blocks of template code inside your templates.
A block defined using these directives can be invoked at processing time by a function call, using the block name as the function name. Essentially, the block directives define a function - by the name that you can specify - that you can then use in subsequent JavaScript code in your template that executes at processing time.
EXAMPLE 1
<%@ startBlock ('fancyRule') %> <%@ endBlock () %>This is section 1.
<%= fancyRule () %>This is section 2.
<%= fancyRule () %>This is section 3.
<%= fancyRule () %>This is section 4.
In the above example, a block called fancyRule
is being defined, that generates output for a decorated rule using a styled div
tag. The block is then used within the rest of the template code to insert the decorated rule into the main output for the template, between each section paragraph.
EXAMPLE 2
<%@ startBlock ('thumbnail','title') %> <% var filename = title.toLowerCase ().replace (/\s+/g,'-'); %> <%@ endBlock () %> <%= thumbnail ('Pink and Yellow Sunset') %> <%= thumbnail ('Braving the Onslaught') %> <%= thumbnail ('Companion to a Sunset') %> <%= thumbnail ('Concrete Eternity') %> <%= thumbnail ('Corrugate It') %>
When defining a block, you can also specify a parameter list. In the above example, a block called thumbnail
is being defined, that takes the single parameter title
. After the block is defined, it is called multiple times with different values for the block's title
parameter. The block uses the parameter in generating its output. The block's function returns the block's generated output. The result of each call to the thumbnail
block's function is being assigned to the template's main output, using the <%=
syntax.
7.1.1. Blocks vs. Functions
For some use cases, you can achieve an effect equivalent to blocks by using function statements inside your template.
So, the same effect as the previous example could also be achieved using the following template...
<% function thumbnail (title) { %> <% var filename = title.toLowerCase ().replace (/\s+/g,'-'); %> <% } %> <% thumbnail ('Pink and Yellow Sunset') %> <% thumbnail ('Braving the Onslaught') %> <% thumbnail ('Companion to a Sunset') %> <% thumbnail ('Concrete Eternity') %> <% thumbnail ('Corrugate It') %>
In this case, the function thumbnail
inside the template is being called several times. Each time it is called, it executes and adds to template's main output.
The fundamentally useful difference between the function approach and using a block is that a function in your template will always add to the template's main output, whereas a block has its own discrete output. This is why the form shown earlier that uses the block needed to use the <%= %>
assignment syntax to assign the result obtained from calling the block to the template's output, whereas this was not necessary with the simple function approach.
The benefit of discrete output with blocks is that your template can then use the block's result in interesting ways, such as encoding it further when assigning it to the template's output, or passing the value to some other function.
EXAMPLE
<%@ input ({idPrefix:'string'}) %> <%@ required ('MyNamespace.Templates.Dialog') %> <%@ startBlock ('dialogContents') %>Renewable energy is energy generated from natural resources such as sunlight, wind, rain, tides and geothermal heat which are renewable (naturally replenished). Renewable energy technologies range from solar power, wind power, hydroelectricity/micro hydro, biomass and biofuels for transportation.
<%@ endBlock () %> <%= MyNamespace.Templates.Dialog.process ({ idPrefix:input.idPrefix, contents:dialogContents ()} ) %>
In the above example, the dialogContents
block is being used to generate contents for a dialog. The output of the block is passed to the process
method of the compiled MyNamespace.Templates.Dialog
template in the contents
property of its input. The result of calling the process
method is then assigned to the template's output. This provides a means for a template to use other templates in its implementation, where those other templates can essentially define content placeholders, and where the template that use them can then build the contents for those content placeholders using block directives.
7.2. What Are Directives, Really?
In contrast to template code that is JavaScript code compiled into the template function and executed at processing time, directives are bits of JavaScript code that are executed at compile time for templates.
They are only relevant at compile time. They have no impact at processing time. Directives are executed within the scope of the Uize.Template.compile
method. As such, directive code has access to functions defined inside the Uize.Template.compile
method's scope. So, for example, by using the @startBlock
directive you are essentially just calling the startBlock
function that is defined within the Uize.Template.compile
method.
Because directives can really be any JavaScript code, you can place JavaScript logic into directives.
EXAMPLE
<%@ var blockName = 'contents'; %> <%@ startBlock (blockName) %> ...my block of stuff... ...my block of stuff... ...my block of stuff... <%@ endBlock () %>
Not the most compelling use of directives, but it illustrates the point that one can execute JavaScript code in directives. In this example, a block by the name of 'contents'
is being defined. This is because the variable blockName
is being defined in a directive before the @startBlock
directive.
Note that the blockName
variable would exist during the compilation process but would not exist at the processing time of the template. So, for example, the following template would still define a block called 'contents'
. That's because the second definition of the blockName
variable is a standard processing time statement and does not get executed during compilation.
<%@ var blockName = 'contents'; %> <% var blockName = 'somethingOtherThanContents'; %> <%@ startBlock (blockName) %> ...my block of stuff... ...my block of stuff... ...my block of stuff... <%@ endBlock () %>
7.3. Other Directives
For a complete list and explanation of all the directive functions - such as the @input
and @required
directives - consult the reference documentation for the Uize.Template
module.
8. Non-significant Whitespace
Whitespace within <%%>
blocks is not significant and does not find its way into a template's output.
The following template blocks are all equivalent in their behavior...
THE FOLLOWING ARE EQUIVALENT
<%.searchParamsJsonStr->!json->urlParams%> <%. searchParamsJsonStr->!json->urlParams %> <% .searchParamsJsonStr->!json->urlParams %> <% .searchParamsJsonStr -> !json -> urlParams %> <%.searchParamsJsonStr -> ! json -> urlParams%>
Blocks can span multiple lines, without affecting a template's output. So, for example, the following template blocks are all equivalent...
THE FOLLOWING ARE EQUIVALENT
<% .searchParamsJsonStr -> !json -> urlParams %> <% .searchParamsJsonStr -> !json -> urlParams %> <% .searchParamsJsonStr -> !json -> urlParams %>
Similarly, whitespace within control (i.e. non-assignment) blocks is non-significant and does not affect output.
9. Comments
Because any contents of non-assignment <%%>
blocks is treated as JavaScript code, you can easily add comments to your templates by simply putting JavaScript style comments inside template blocks.
9.1. Note Style Comments
Note style comments are comments that are purely intended to provide information / annotations to a JavaScript template.
EXAMPLE
<% /* This is a template for a category search link */ %> <% .searchParams.category %> <% // this is the link text %>
As you can see from the example above, you can use both /* */
and //
style comments.
9.2. Commenting Out Template Sections
You can also use comments to comment out sections of a template, as follows...
<% .searchParams.category %> <% /* not yet ready to use this CSS styling %> <% .searchParams.category %> <% */ %>
In the above example, the decoration of the category name inside a styled span
tag has been commented out using an enclosing /* */
style comment.
9.3. Comments Inside Directives
Code inside directives is also JavaScript code, so you can use directives as another place to stash comments for your JavaScript templates.
EXAMPLE
<%@ /* This is a template for a category search link */ %> <% .searchParams.category %> <% // this is the link text %>
Whereas comments inside normal non-assignment <%%>
blocks will actually appear inside the code of compiled JavaScript templates, comments inside directives will not, since directives are executed (and "consumed") during compilation time and directive code doesn't appear inside a template's compiled code.
9.4. HTML Comments
Of course, there is nothing to stop you from including HTML style comments inside a JavaScript template - assuming the template is intended to output HTML.
EXAMPLE
<% .searchParams.category %> <% // this is the link text %>
HTML comments, like the one above, will appear in the actual generated output of a template. There is really nothing special about it - it's merely a static part of the template's output.
10. Advanced Topics
10.1. Alternate Token Syntax
While the default behavior of the UIZE JavaScript Template system assumes the delimiters <%
and %>
for enclosing code and assignment blocks, it is possible to compile templates using different tokens by specifying a value for the optional templateOptions
parameter when calling the Uize.Template.compile
method.
The openerToken
and closerToken
properties of the templateOptions
object let you configure the block delimiter tokens.
EXAMPLE
var messageRepeatedTemplate1 = Uize.Template.compile ( '<% for (var i = 0; i < input.repeats; i++) { %><%.message%>
<% } %>' ), messageRepeatedTemplate2 = Uize.Template.compile ( '[% for (var i = 0; i < input.repeats; i++) { %][%.message%]
[% } %]', {openerToken:'[%',closerToken:'%]'} ), messageRepeatedTemplate3 = Uize.Template.compile ( '[# for (var i = 0; i < input.repeats; i++) { #][#.message#]
[# } #]', {openerToken:'[#',closerToken:'#]'} ) ;
In the above example, messageRepeatedTemplate1
, messageRepeatedTemplate2
, and messageRepeatedTemplate3
are all equivalent and would produce the same results when called.