Client and server-side templating with Velocity
By Sing Li2004-06-13
Basic template engine operation
The operation of a basic templating engine is quite straightforward. First consider the template in Listing 1:
Listing 1. A basic Velocity template
<html>
<head>
<title>A Template Based Page</title≶
</head>
<body>
<p>This is a page generated by $generatedBy.</p>
<p>The customer's name is $customerName.</p>
</body>
</html>
This template is a complete HTML file. You can create it using a text editor or a favorite graphical visual Web page editor. This ease of creation is the prime benefit and appeal of a template-based system.
The color highlighted area in the Listing 1 template will be replaced by actual data when the templating engine runs. The process of taking the data and combining it into a template is called merging. Consider the data represented in the script in Listing 2:
Listing 2. Setting data values for template merging
#set ($generatedBy = "Velocity")
#set ($customerName = "John Doe")
Now, if the Listing 1 template is merged with the Listing 2 data, the result will be as shown in Listing 3:
Listing 3. Data merged with template
<html>
<head>
<title>A Template Based Page</title>
</head>
<body>
<p>This is a page generated by Velocity.</p>
<p>The customer's name is John Doe.</p>
</body>
</html>
You may recognize this feature as being similar to the mail merge feature in a word processor, in which form letters are merged with names and addresses from a mailing list. As with mail merge, the application is most useful when the data source being merged is large and varied.
Velocity is a templating engine in this pure sense. The output format of Velocity is limited only by what can be placed in a text template. This includes the most popular formats today (HTML, XML, SQL, and so on).
Creating templates with the Velocity Template Language
Velocity templates are text files (HTML, XML, and so on) containing:
• Static portions that will be merged as-is
• Placeholders that will be replaced with merged data
• Directives and instructions in a scripting language
The scripting language used with Velocity templates is known as the Velocity Template Language (VTL). The VTL syntax is relatively lean when compared to other scripting languages. For anyone with a programming background, learning VTL should be a very quick task.
Placeholders and references
A reference in VTL is a named element such as $customerName. References can act as data placeholders in Velocity templates. These placeholders are replaced with their textual value during the template merge to create the final output. For example, back in Listing 1, we can see two VTL references ($generatedBy and $customerName) used to create the final output.
A variable is one type of reference in VTL. You can use the #set() directive to assign values to variables. Listing 4 shows some examples:
Listing 4. Variable typed VTL references
#set( $this = "Velocity")
#set( $numericBase = 999 )
#set( $booleanCondition = true )
This page is generated using $this.
There are ($numericBase + 1) pages in total.
Variable names must begin with a letter, which makes it easy for Velocity to distinguish variable names from currency notation within a template (for instance, $100 cannot be a variable name). All variables are converted to strings during the merge operation, which can create some interesting situations. Consider the text highlighted in red in Listing 4. The merged output is shown in Listing 5:
Listing 5. Variable with numeric value in merged template
This page is generated using Velocity.
There are (999 + 1) pages in total.
Because $numericBase is converted to a string during the merge operation, no arithmetic operation is performed. Because it's specialized for template operations and is not a general computing language, VTL needs to support only integer math (although plug-in tools can be used to extend this). The script below reveals how to work with this math capability:
#set( $newSum = $numericBase + 1)
There are $newSum pages in total.
The corresponding output after merging this template will be:
There are 1000 pages in total.
Thus far, we have only dealt with scalar variables. To create an ArrayList variable consisting of multiple elements, use this syntax:
#set( $treeList = ["pine", "oak", "maple", "redwood"])
You can obtain the second item in the list by using $treeList.get(1).
After the assignment, $treeList is an ArrayList-based variable (as in the standard JDK collection class). You can access each of its elements directly using the notation $treeList.get(n), where n is a zero-based index ArrayList. For example, as indicated by the red highlighted line in Listing 6, $treeList.get(1) is used to select the second item in the ArrayList: oak. This syntax of invoking a method of the ArrayList class can be used to invoke methods of other available objects (see the Property and method references sidebar for more information).
Property and method references
Other than variables that we set within the template, VTL references can also be object properties or methods. These objects are Java classes that are made available to the template (typically through the context, see Velocity contexts).
Properties of objects are accessed through similar JavaBean notations. For example, you can access the LastName property of the $customer object through the VTL reference $customer.LastName. Under the hood, Velocity uses an object's accessor method to obtain the property value (that is, the getLastName() method of the object is invoked).
You can invoke methods of an object, with or without a parameter list, with a familiar notation to property access. For example, you may call the getPhone() method of the $customer object to obtain a mobile phone number using the VTL reference $customer.getPhone("mobile").
Now, a word about placeholder substitution: Velocity will print any unrecognized reference as plain text, as illustrated by the next two highlighted lines (blue and green) in Listing 6:
Listing 6. Placeholder substitution
The second item in the list is $treeList.get(1).
$notDeclared is an undeclared variable.
But $!notDeclared is invisible when not declared.
VTL supports a quiet reference notation to avoid rendering nonexistent or null references. If you use the quiet reference notation, as in $!notDeclared, then Velocity will render nothing to the output instead of the full reference name. Note the "!" before the variable name to signify the quiet reference notation. When the Listing 6 template is merged, both references are not assigned any value, but the blue highlighted reference will be rendered as-is, while the green one will be invisible:
The second item in the list is oak.
$notDeclared is an undeclared variable.
But is invisible when not declared.
Selective rendering and repetition
You can use the #if... #then... #else.... directive to conditionally render a certain portion of a template. Listing 7 shows an example:
Listing 7. Selective rendering using #if, #then, and #else
#if $customer.GoldMember
Thank you Mr. $customer.LastName, for flying with us.
Your loyal patronage is greatly appreciated.
This flight earns you an additional 5000 miles.
#else
Thank you for flying with us.
Please consider joining our frequent flyer program.
#endif
In the Listing 7 template, the boolean GoldMember property of the $customer object is used to determine which message to show on the final output. For a gold member, the blue highlighted message will be rendered; otherwise, the green highlighted message will be displayed in the final output.
Repetition is required frequently in templates to format information in a tabular or list format. The data displayed is typically kept in an ArrayList reference. The only directive for handling repeated looping in Velocity is the #foreach directive. The Listing 8 template illustrates the use of the #foreach directive with our $treeList ArrayList variable. Of course, any other available collection-typed object reference, or an object property/method reference returning a collection, may also be used.
Listing 8. Repetition using #foreach
<table>
<tr><td>Tree Name</td></tr>
#foreach $name in $treeList
<tr><td>
$name is a big tree!
</td>≷/tr>
#end
</table>
With the $treeList containing the list of tree names, the merged output of the Listing 8 template will look like Listing 9:
Listing 9. Merged output from the #foreach loop
<table>
<tr><td>Tree Name</td></tr>
<tr><td>
pine is a big tree!
</td></tr>
<tr><td>
oak is a big tree!
</td></tr>
<tr><td>
maple is a big tree!
</td></tr>
<tr><td>
redwood is a big tree!
</td></tr>
</table>
When viewed from an HTML browser, of course, Listing 9 is a table of tree names.
Note that there is a "built-in" counter inside the body of a #foreach loop. It is accessible through the $velocityCounter reference within the body of the #foreach directive. By default, this counter starts at 1 and is incremented each time the loop is taken.
Macros in Velocity
One main feature of Velocity is its ability to easily define macros, called Velocimacros. Macros enable you to encapsulate template scripts and reuse them easily. By default, they are kept in a VM_global_library.vm file. For example, consider the Velocimacro called #showTree() in Listing 10:
Listing 10. Defining a Velocimacro
#macro (showTree)
#if ($treeList )
#foreach ($e in $treeList )
$e
#end
#end
#end
You can invoke the #showTree() Velocimacro and use it to print out the $treeList ArrayList, provided the list is defined. The invocation syntax is simply #showTree().
It is also possible for you to parameterize a macro. For example, we can modify the #showTree() macro to work on any list, as shown in Listing 11:
Listing 11. A Velocimacro with parameters
#macro (showList $val)
#if ($val )
#foreach ($e in $val )
$e
#end
#end
#end
To invoke the Listing 11 macro with our $treeList, we can use #showList($treeList). The output will be identical in either case, as shown in Listing 12:
Listing 12. Merged output from Velocimacro
pine
oak
maple
redwood
Other interesting VTL details
Single-line comments or end-of-line comments are preceded by ##. Multi-line comments are bracketed by the #* and *# pair.
When working with string data, you can use either double quotation marks or single quotation marks to delimit them. However, the use of double quotation marks allows evaluation of Velocity references, directives, or even Velocimacros inside the delimited string.
Tutorial Pages:
» Client and server-side templating with Velocity
» Basic template engine operation
» Velocity contexts
» Velocity as a standalone parser
» Velocity vs. JSP technology on the server
» Deploying Velocity with Tomcat 5
» Interoperating with the Struts framework
» Conclusions
» Resources
First published by IBM developerWorks
