The Request Lifecycle
The templating feature of Chrome Server is designed to help separate your programming logic (code) from the markup (design) of the page that you are going to display.
When Chrome Server received a request from a web browser, it first runs the scripts (a global script and a page script), then it takes the results of running the scripts and passes them to a page template. The result of applying the page template to the results of the scripts is a web page that is returned to the client's browser.
Keeping code and markup apart offers you several benefits:
Chrome Server doesn't absolutely force you to follow this approach - there are hooks in the response API that allow you to include markup directly in your code, but it is very much encouraged and we have tried to make it easy for you to do things the "right way".
At its simplest a template is raw html or other content that you want to return directly to the user's browser as the result of a request. In general any valid html page is a valid template - but its contents are fixed.
To allow the contents of a page to be dictated by the results of running a script special templating placeholders and directives must be used in the page content.
For an example, consider the following page script:
var greeting = 'Hello World';
var total = 1;
for( var i = 1; i <= 10; i++ ) {
total *= i;
}
var truth = false;
Three global Javascript variables are updated - greeting, total, and truth (the variable i used in the loop is a local, not a global, variable).
We will now create a page template to display these variables in the associated page template. The following is normal HTML except for the highlighted parts:
<html>
<head>
<title>Simple Template</title>
</head>
<body>
<p>Greeting: <b>${greeting}</b></p>
<p>Total: <b>${total}</b></p>
<p>Truth: <b>${truth?string("true", "false")}</b></p>
</body>
</html>
When Chrome Server receives a request for this page the page script will be run, creating, calculating, and populating the three variables. They will then be handed to the page template which will produce HTML containing their values. The result from the user's point of view will be that the body of the page contains the following output:
Greeting: Hello World Total: 3,628,800 Truth: false
Of course this is a trivial example - normally we won't put fixed content in the output; you will want to store and retrieve database data, perform other calculations, and manipulate form data - the above example shows all the basic steps required for a page template though. Any of the information that you can store in a Javascript variable can be presented on the page using these techniques.
When a substitution variable is used for which there is no corresponding Javascript variable, no output will be produced. Instead a warning message will be logged in the logs panel of the administration application. We recommend keeping an eye on these logs and fixing code that causes warnings of this type to appear.
We will now look more closely at the substitution variables from the earlier page template example.
As shown in the preceding section, substitution variables allow you to populate the page displayed to the user with the contents of Javascript variables. The specific behaviour of substitution variables depends upon the type of the variable, however. The following is a list of the common Javascript types:
In the following sections will now discuss how substitution variables handle these variable types:
The basic format for a substitution variable is ${name} where name is the name of the Javascript variable to be displayed. This works fine for string and number values as you can see in our example for the greeting and total variables where we include them as ${greeting} and ${total} respectively.
See the later section on string operators for some additional options when displaying string values.
Our initial example included a boolean value. Because the meaning of a boolean usually depends upon the context that it is used in (for example on/off, true/false, yes/no), Chrome Server doesn't display anything if you try the obvious format (i.e. ${truth} will not work in the way that you might expect).
Instead, for booleans you must use the format ${name?string}. The ?string converts the boolean value to a default string of "true" or "false". If these are not acceptable representations you can then over-ride the defaults by providing a pair of replacement string values thus: ${name?string("yes","no")}.
Our example uses a substitution variable with the format ${truth?string("true", "false")} which lists the values explicitly rather than using the equivalent default.
Some alternative representations would be:
Values from arrays can be extracted using simple index values. For example, consider an array containing the names of the days of the week as defined in the following page script:
var daysOfTheWeek = ['Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday'];
To access a value from this array we use the (zero-offset) index into the list. For example to display Monday and Wednesday we could use the following substitutions:
Javascript's distinction between arrays and objects is quite nebulous and as we will now show, object properties can be accessed with a very similar syntax.
In Javascript we can create a new object with the new operator and than attach properties to it. To do this we can either use the dot operator or the square bracket (array) operators. When using the square bracket operators the property name can be specified in single or double quotes. The following Javascript code snippet illustrates all of these techniques:
var user = new Object(); user.name = 'David'; user['email'] = 'dave@chromeserver.com'; user["hobby"] = 'Hopping';
Happily you can access these values in the page script using substitution variables of exactly the same form, so that to display the values from the page script shown above you could use the following page template contents:
<p>User's Name: ${user.name}</p>
<p>User's Email: ${user['email']}</p>
<p>User's Hobby: ${user["hobby"]}</p>
There are a variety of other ways to create objects and properties in Java - the mechanism used is not relevant to the page script as you will always use one of the three mechanisms shown above to access the value.
Note that you cannot use this syntax to call functions on the Javascript objects - if you need to display the result of a function call on an object you must access it within the server side script and make it available as a global variable or object property.
The special Javascript values null and undefined can not be displayed directly, but can be handled with directives.
The entries in your template that control conditional and looping (repeated) display of content are called directives.
In general we want to discourage the intermingling of code and markup because doing so makes an application much harder to understand and maintain. Sometimes, however, we need to put some logic into the page. Particularly, we will sometimes want to display content conditionally and we will sometimes want to repeat some content for each of an array of items. Freemarker provides several sorts of conditional logic and a loop, so you can manage this sort of content without having to resort to putting content into your scripts.
Directives are generally indicated using markers of the form <#keyword parameters > where the keyword indicates the purpose of the directive and the parameters (often having the same form as the part of a substitution variable within the curly braces) controls its behaviour.
Conditions allow you to display content only under some circumstances relating to the script variables. The simplest condition is the if/elseif/else condition. The terms of the directive serve the same purpose as the if/else statements in Javascript. The "else if" and "else" parts of the directive can be omitted as appropriate, but the general form of this directive is as follows:
<#if condition> content <#elseif condition> content <#elseif condition> content <#else> content </#if>
The content value is any content that would normally appear in a page template, and you are permitted to nest directives. The conditions are combine the inner part of the substitution variables with operators to establish a boolean result. In the following example we display content only if the user has a particular age:
<#if user.age == 42> <p>User is aged forty two</p> </#if>
The if/elseif/else directives are useful for dealing with complex logic, but sometimes you will want to select behaviour based upon the value of a single variable. In these cases building the appropriate directive from if/elseif/else blocks becomes inconveniently verbose and so the alternative switch/case/break/default directives can be used to achieve the same effect in a more terse manner:
<#switch value>
<#case literal>
content
<#break>
<!-- Case section(s) repeated as often as necessary -->
<#case literal>
content
<#break>
<#case literal>
content
<#break>
<#default>
content
</#switch>
Again the content value is any content that would normally appear in a page template, and you are permitted to nest directives. The value entry in the switch is the name of a Javascript variable or property, or some other expression. See the section below for more on the operators you can use to build expressions. The literal values are quoted strings, numeric values, or boolean values that the value expression will be compared against. In the following example we use the user role to determine the greeting that will be presented to them:
<#switch user.role>
<#case "SYSTEM">
<h1>Welcome Superuser</h1>
<#break>
<#case "ADMINISTRATOR">
<h1>Welcome Site Administrator</h1>
<#break>
<#default>
<h1>Welcome ${user.name}</h1>
</#switch>
Note that the two types of conditional directive are completely interchangeable and you should use whichever is the most convenient when writing your page templates.
The list directive allows you to process the contents of array variables and display their contents.
<#list array as name>
content
</#list>
The loop will iterate over each element in the named array making it available as the given name variable.
As usual the content value is any content that would normally appear in a page template, and you are permitted to nest directives.
As well as the named variable, the list directive sets up a pair of additional variables. These are given names based upon the name you supply in the directive; for example if you provide a name of entry in the directive they will be called entry_index and entry_has_next
The ..._index variable provides a (zero offset) index of the array element that is being processed. The ..._has_next variable is a boolean indicating whether the entry being processed is the last in the array or not. These are typically useful when numbering and otherwise formatting lists.
For example, the following page script establishes an array variable called directions
var directions = [ "Up", "Down", "Left", "Right"];
The following page template content then lists each entry in the array along with its offset index and a flag indicating whether or not there are any entries remaining in the array to be processed:
<#list directions as direction>
<p> ${direction_index} : ${direction_has_next?string("yes","no")} : ${direction} </p>
</#list>
The resulting output is as follows:
0 : yes : Up 1 : yes : Down 2 : yes : Left 3 : no : Right
As well as processing Javascript arrays you can also process specific number ranges using the syntax n..m where n and m are both decimal numbers. For example, to display the numbers one to ten independently of any Javascript variables:
<#list 1..10 as decimal>
<p>${decimal}</p>
</#list>
If you want to exit the loop before processing of all the array elements has completed, you can make use of the break directive and a suitable condition. For example:
<#list directions as direction>
<#if direction = "Unknown">
<p>Instructions contain an unknown direction!</p>
<#break>
</if>
<p>${direction}</p>
</#list>
Note that page templates that involve breaking out of loops are intrinsically more difficult to understand than ones which simply iterate over all of the elements, so we recommend using the break directive only when absolutely necessary within loops.
While the looping directive is somewhat primitive compared to Javascript's capabilities it should be more than sufficient for basic page formatting requirements. Where you want to achieve more complex results you will generally find that the best approach is to populate an array in the page script with a set of objects containing additional information that can then be used to control the page template's rendered output.
The operators available for use in directives are summarized in the following tables.
Some operators are applicable to all Javascript variable types - these are the operators which compare to variables, or which check to see if a value has been assigned to the variable in question.
| Operator | Description | Example |
|---|---|---|
| = | Equals | <#if x = 42>...</#if> |
| == | Equivalent to = | <#if x == 42>...</#if> |
| != | Not equivalent to | <#if x != 42>...</#if> |
| ?? | Missing Value Test | <#if x??><p>X is set</p></#if> |
Note that the page templating = and == symbols perform the equivalent of the Javascript === operation.
Booleans represent single logical bits, typically representing true/false or yes/no values. Booleans cannot be displayed directly as substitution variables. Instead the built in ?string function should be used. See the boolean section of the substitution variable documentation for details.
| Operator | Description | Example |
|---|---|---|
| || | Logical or | <#if x = 42 | x = 24>...</#if> |
| && | Logical and | <#if x = 42 && y = 42>...</#if> |
| ! | Not | <#if !(x = 42)>...</#if> |
There are no bitwise operators for boolean values; if you need to perform bitwise operations do so in the Javascript logic and pass the results as variables to the page script. If you're not familiar with the distinction between bitwise and logical operations you will probably find that the logical operators are sufficient anyway.
As you might expect there is a wealth of operators for manipulating and comparing numeric values. All of the basic arithmetic operators are available to you along with some special cases due to the html-like nature of page templates.
| Operator | Description | Example |
|---|---|---|
| < | Less than | <#if (age < 18)><p>Child</p></#if> |
| lt | Equivalent to < | <#if age lt 18><p>Child</p></#if> |
| > | Greater than | <#if (age > 17)><p>Adult</p></#if> |
| gt | Equivalent to > | <#if (age gt 17)><p>Adult</p></#if> |
| <= | Less than or equal to | <#if (manager.age <= employee.age)>Younger</#if> |
| lte | Equivalent to <= | <#if manager.age lte employee.age>Younger</#if> |
| >= | Greater than or equal to | <#if manager.age >= employee.age>Older</#if> |
| gte | Equivalent to >= | <#if manager.age gte employee.age>Older</#if> |
| * | Multiply | <#if (x * 100) gt 1>...</#if> |
| / | Division | <#if (x / 100) gt 1>...</#if> |
| % | Modulo Division (remainder) | <#if x % 100 = 0>...</#if> |
| ?int | Integer part | <#if x?int = 1>...</#if> |
The lt, lte, gt and gte operators exist because of the potential for ambiguity or confusion with the angle brackets used to commence and terminate directives. In general these forms are preferable to their equivalent < , <= , > , and >= forms but the latter can be used even where they might otherwise be ambiguous as long as they are contained within parentheses.
For example the directive <#if x > 42>...</#if> is invalid because it is not possible to tell which > symbol terminates the condition, whereas both of <#if (x > 42)>...</#if> and <#if x gt 42>...</#if> are valid and unambiguous.
The following operators apply to any string variables or literals (though there is rarely a need for them in the latter case):
| Operator | Description | Example |
|---|---|---|
| ?html | Escape < and & symbols | ${name?html} |
| ?cap_first | Capitalize first letter | ${surname?cap_first} |
| ?lower_case | Convert to lower case | ${tag?lower_case} |
| ?upper_case | Convert to upper case | ${postcode?upper_Case} |
| ?trim | Trim leading and trailing spaces | ${name?trim} |
These operators can be chained as you would expect, so to take an arbitrary name string and force the first letter to be upper case and the rest in lower case with no leading or trailing spaces you could use a substitution variable of the form ${name?lower_case?cap_first?trim}
Normally if you want to concatenate strings in output you will simple put suitable placeholder variables together (e.g. <p>${user.forename} ${user.surname}</p> ) but you can also use an over-ridden form of the + operator on string values thus <p>${ user.forename + " " + user.surname }</p> and the operator can be used when forming conditions and other expressions as well.
The normal processing of arrays is carried out using the list directive so there is little need for array operators. Currently we provide only a mechanism for determining the size of the array.
| Operator | Description | Example |
|---|---|---|
| ?size | Size of array | <#if (l?size > 0)>...</#if> |
This capability is most often useful determining whether an array is empty (as shown in the table) or when producing "Item n of m" indicators, which might typically be rendered using template content as shown in the following example:
<#list books as book>
<p>Result ${book_index + 1} of ${books?size}: ${book.title} by ${book.author}</p>
</#list>
For more complicated processing of array contents you will generally need to perform processing in the page script and then publish that information to the page template as additional Javascript variables.