9 min read

Extracting the templates

The first step to modifying an existing theme or creating our own is to extract the templates from the Struts 2 distribution. This actually has the advantageous performance side effect of keeping the templates in the file system (as opposed to in the library file), which allows FreeMarker to cache the templates properly. Caching the templates provides a performance boost and involves no work other than extracting the templates. The issue with caching templates contained in library files, however, will be fixed.

If we examine the Struts 2 core JAR file, we’ll see a /template folder. We just need to put that in our application’s classpath. The best way to do this depends on your build and deploy environment. For example, if we’re using Eclipse, the easiest thing to do is put the /template folder in our source folder; Eclipse should deploy them automatically.

A maze of twisty little passages

Right now, consider a form having only text fields and a submit button. We’ll start by looking at the template for the text field tag. For the most part, Struts 2 custom tags are named similarly to the template file that defines it. As we’re using the “xhtml” theme, we’ll look in our newly-created /template/xhtml folder. Templates are found in a folder with the same name as the theme.

We find the <s:textfield> template in /template/xhtml/text.ftl file. However, when we open it, we are disappointed to find it implemented by the following files—controlheader.ftl file retrieved from the current theme’s folder, text.ftl from the simple theme, and controlfooter.ftl file from “xhtml” theme. This is curious, but satisfactory for now.

We’ll assume what we need is in the controlheader.ftl file. However, upon opening that, we discover we actually need to look in controlheader-core.ftl file. Opening that file shows us the table rows that we’re looking for.

Going walkabout through source code, both Java and FreeMarker, can be frustrating, but ultimately educational. Developing the habit of looking at framework source can lead to a greater mastery of that framework. It can be frustrating at times, but is a critical skill.

Even without a strong understanding of the FreeMarker template language, we can get a pretty good idea of what needs to be done by looking at the controlheader-core.ftl file. We notice that the template sets a convenience variable (hasFieldErrors) when the field being rendered has an error. We’ll use that variable to control the style of the table row and cells of our text fields. This is how the class of the text field label is being set.

Creating our theme

To keep the template clean for the purpose of education, we’ll go ahead and create a new theme. (Most of the things will be the same, but we’ll strip out some unused code in the templates we modify.) While we have the possibility of extending an existing theme (see the Struts 2 documentation for details), we’ll just create a new theme called s2wad by copying the xhtml templates into a folder called s2wad. We can now use the new theme by setting the theme in our <s:form> tag by specifying a theme attribute:

<s:form theme="s2wad" ... etc ...>

Subsequent form tags will now use our new s2wad theme. Because we decided not to extend the existing “xhtml” theme, as we have a lot of tags with the “xhtml” string hard coded inside. In theory, it probably wasn’t necessary to hard code the theme into the templates. However, we’re going to modify only a few tags for the time being, while the remaining tags will remain hard coded (although incorrectly). In an actual project, we’d either extend an existing theme or spend more time cleaning up the theme we’ve created (along with the “xhtml” theme, and provide corrective patches back to the Struts 2 project).

First, we’ll modify controlheader.ftl to use the theme parameter to load the appropriate controlheader-core.ftl file. Arguably, this is how the template should be implemented anyway, even though we could hard code in the new s2wad theme.

Next, we’ll start on controlheader-core.ftl. As our site will never use the top label position, we’ll remove that. Doing this isn’t necessary, but will keep it cleaner for our use. The controlheader-core.ftl template creates a table row for each field error for the field being rendered, and creates the table row containing the field label and input field itself.

We want to add a class to both the table row and table cells containing the field label and input field. By adding a class to both, the row itself and each of the two table cells, we maximize our ability to apply CSS styles. Even if we end up styling only one or the other, it’s convenient to have the option.

We’ll also strip out the FreeMarker code that puts the required indicator to the left of the label, once again, largely to keep things clean. Projects will normally have a unified look and feel. It’s reasonable to remove unused functionality, and if we’re already going through the trouble to create a new theme, then we might as well do that.

We’re also going to clean up the template a little bit by consolidating how we handle the presence of field errors. Instead of putting several FreeMarker <#if> directives throughout the template, we’ll create some HTML attributes at the top of the template, and use them in the table row and table cells later on.

Finally, we’ll indent the template file to make it easier to read. This may not always be a viable technique in production, as the extra spaces may be rendered improperly, (particularly across browsers), possibly depending on what we end up putting in the tag. For now, imagine that we’re using the default “required” indicator, an asterisk, but it’s conceivable we might want to use something like an image. Whitespace is something to be aware of when dealing with HTML.

Our modified controlheader-core.ftl file now looks like this:

<#assign hasFieldErrors = parameters.name?exists && fieldErrors?exists &&
fieldErrors[parameters.name]?exists/>
<#if hasFieldErrors>
<#assign labelClass = "class='errorLabel'"/>
<#assign trClass = "class='hasErrors'"/>
<#assign tdClass = "class='tdLabel hasErrors'"/>
<#else>
<#assign labelClass = "class='label'"/>
<#assign trClass = ""/>
<#assign tdClass = "class='tdLabel'"/>
</#if>

<#if hasFieldErrors>
<#list fieldErrors[parameters.name] as error>
<tr errorFor="${parameters.id}" class="hasErrors">
<td>&nbsp;</td>
<td class="hasErrors"><#rt/>
<span class="errorMessage">${error?html}</span><#t/>
</td><#lt/>
</tr>
</#list>
</#if>

<tr ${trClass}>
<td ${tdClass}>
<#if parameters.label?exists>
<label <#t/>
<#if parameters.id?exists>
for="${parameters.id?html}" <#t/>
</#if>
${labelClass}
><#t/>
${parameters.label?html}<#t/>
<#if parameters.required?default(false)>
<span class="required">*</span><#t/>
</#if>
:<#t/>
<#include "/${parameters.templateDir}/s2e2e/tooltip.ftl" />
</label><#t/>
</#if>
</td><#lt/>

It’s significantly different when compared to the controlheader-core.ftl file of the “xhtml” theme. However, it has the same functionality for our application, with the addition of the new hasErrors class applied to both the table row and cells for the recipe’s name and description fields. We’ve also slightly modified where the field errors are displayed (it is no longer centered around the entire input field row, but directly above the field itself).

We’ll also modify the controlheader.ftl template to apply the hasErrors style to the table cell containing the input field. This template is much simpler and includes only our new hasErrors class and the original align code. Note that we can use the variable hasFieldErrors, which is defined in controlheader-core.ftl. This is a valuable technique, but has the potential to lead to spaghetti code. It would probably be better to define it in the controlheader.ftl template.

<#include "/${parameters.templateDir}/${parameters.theme}/
controlheader-core.ftl" />
<td
<#if hasFieldErrors>
class="hasErrors"<#t/>
</#if>
<#if parameters.align?exists>
align="${parameters.align?html}"<#t/>
</#if>
><#t/>

We’ll create a style for the table cells with the hasErrors class, setting the background to be just a little red. Our new template sets the hasErrors class on both the label and the input field table cells, and we’ve collapsed our table borders, so this will create a table row with a light red background.

.hasErrors td {
background: #fdd;
}

Now, a missing Name or Description will give us a more noticeable error, as shown in the following screenshot:

Themes and Templates with Apache Struts 2

This is fairly a simple example. However, it does show that it’s pretty straightforward to begin customizing our own templates to match the requirements of the application. By encapsulating some of the view layer inside the form tags, our JSP files are kept significantly cleaner.

Other uses of templates

Anything we can do in a typical JSP page can be done in our templates. We don’t have to use Struts 2’s template support. We can do many similar things in a JSP custom tag file (or a Java-based tag), but we’d lose some of the functionality that’s already been built.

Some potential uses of templates might include the addition of accessibility features across an entire site, allowing them to be encapsulated within concise JSP notation. Enhanced JavaScript functionality could be added to all fields, or only specific fields of a form, including things such as detailed help or informational pop ups. This overlaps somewhat with the existing tooltip support, we might have custom usage requirements or our own framework that we need to support.

Struts 2 now also ships with a Java-based theme that avoids the use of FreeMarker tags. These tags provide a noticeable speed benefit. However, only a few basic tags are supported at this time. It’s bundled as a plug-in, which can be used as a launching point for our own Java-based tags.

Summary

Themes and templates provide another means of encapsulating functionality and/or appearance across an entire application. The use of existing themes can be a great benefit, particularly when doing early prototyping of a site, and are often sufficient for the finished product.

Dealing effectively with templates is largely a matter of digging through the existing template source. It also includes determining what our particular needs are, and modifying or creating our own themes, adding and removing functionality as appropriate. While this article only takes a brief look at templates, it covers the basics and opens the door to implementing any enhancements we may require.

If you have read this article you may be interested to view :

LEAVE A REPLY

Please enter your comment!
Please enter your name here