Lesson 4.1.1: WebWork UI Tags

In WebWork, the UI tags wrap generic HTML controls while providing tight integration with the core framework. The tags have been designed to minimize the amount of logic in compiled code and delegate the actual rendering of HTML to a template system. The UI tags attempt to cover the most common scenarios, while providing a Component Tag for creating custom components. The UI tags also provide built-in support for displaying inline error messages.

This lesson tries to explain how to take advantage of the UI tags to build forms and other graphical controls and, by explaining how the template system works, teaches you how to change the look of existing components and create your own UI components.

Building forms:

WebWork comes with ready-to-use tags to construct forms. Some of these tags relate directly to HTML tags that are used to make forms and you probably can figure them out by their names: <ww:checkbox />, <ww:file />, <ww:form />, <ww:hidden />, <ww:label />, <ww:password />, <ww:radio />, <ww:select />, <ww:submit />, <ww:textarea /> and <ww:textfield />.

To build forms with these tags, place them in your page as you would do with the HTML tags. The only difference is that the parameters should be enclosed in double quotes and single quotes (key="'value'"). That's because names that are not single-quoted are evaluated against the Value Stack.

Let's check out an example:

ex01-index.jsp:

<%@ taglib uri="webwork" prefix="ww" %>
<html>
<head>
<title>WebWork Tutorial - Lesson 4.1.1 - Example 1</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css">
	.errorMessage { color: red; }
</style>
</head>

<body>

<p>UI Form Tags Example:</p>

<ww:form action="'formProcessing.action'" method="'post'">
	<ww:checkbox name="'checkbox'" label="'A checkbox'" fieldValue="'checkbox_value'" />
	<ww:file name="'file'" label="'A file field'" />
	<ww:hidden name="'hidden'" value="'hidden_value'" />
	<ww:label label="'A label'" />
	<ww:password name="'password'" label="'A password field'" />
	<ww:radio name="'radio'" label="'Radio buttons'" list="{'One', 'Two', 'Three'}" />
	<ww:select name="'select'" label="'A select list'" list="{'One', 'Two', 'Three'}" 
		emptyOption="true" />
	<ww:textarea name="'textarea'" label="'A text area'" rows="'3'" cols="'40'" />
	<ww:textfield name="'textfield'" label="'A text field'" />
	<ww:submit value="'Send Form'" />
</ww:form>

</body>
</html>

HTML result after processing ex01-index.jsp:

<html> 
<head> 
<title>WebWork Tutorial - Lesson 4.1.1 - Example 1</title>
<style type="text/css"> 
  .errorMessage { color: red; } 
</style>   
</head> 

<body> 

<p>UI Form Tags Example:</p> 

<table> 
<form 
action="formProcessing.action" method="post" > 

   


<tr> 
<td valign="top" colspan="2"> 

<table width="100%" border="0" cellpadding="0" cellspacing="0"> 
<tr><td valign="top"> 
<input type="checkbox" 
name="checkbox" 
value="checkbox_value" 
/> 
</td> 
<td width="100%" valign="top"> 
<span class="checkboxLabel"> 
A checkbox 
</span> 
</td> 
</tr> 
</table> 
</td> 
</tr> 

   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A file field: 
</span> 
</td> 

<td> 

<input type="file" 
name="file" 
/> 

</td> 
</tr> 

  <input 
type="hidden" 
name="hidden" value="hidden_value" /> 
   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A label: 
</span> 
</td> 

<td> 
<label> </label> 
</td> 
</tr> 

   




<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A password field: 
</span> 
</td> 

<td> 

<input type="password" 
name="password" 


/> 

</td> 
</tr> 

   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

Radio buttons: 
</span> 
</td> 

<td> 





<input 
type="radio" 
name="radio" 
id="radioOne" 
value="One" /> 
<label for="radioOne">One</label> 





<input 
type="radio" 
name="radio" 
id="radioTwo" 
value="Two" /> 
<label for="radioTwo">Two</label> 





<input 
type="radio" 
name="radio" 
id="radioThree" 
value="Three" /> 
<label for="radioThree">Three</label> 


</td> 
</tr> 

   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A select list: 
</span> 
</td> 

<td> 

<select name="select" 
> 


<option value=""></option> 





<option value="One" 
>One</option> 





<option value="Two" 
>Two</option> 





<option value="Three" 
>Three</option> 


</select> 

</td> 
</tr> 

   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A text area: 
</span> 
</td> 

<td> 

<textarea name="textarea" 
cols="40" 
rows="3" 
></textarea> 

</td> 
</tr> 

   



<tr> 
<td align="right" valign="top"> 

<span class="label"> 

A text field: 
</span> 
</td> 

<td> 

<input type="text" 
name="textfield" 
/> 

</td> 
</tr> 

  <tr> 
<td colspan="2"><div 
align="right" ><input 
type="submit" 
value="Send Form" /></div> 
</td> 
</tr> 

</form> 
</table> 


</body> 
</html>

xwork.xml:

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" 
"http://www.opensymphony.com/xwork/xwork-1.0.dtd">

<xwork>
	<!-- Include webwork defaults (from WebWork JAR). -->
	<include file="webwork-default.xml" />
	
	<!-- Configuration for the default package. -->
	<package name="default" extends="webwork-default">
		<action name="formProcessing" class="lesson04_01_01.FormProcessingAction">
			<result name="input" type="dispatcher">ex01-index.jsp</result>
			<result name="success" type="dispatcher">ex01-success.jsp</result>
			<interceptor-ref name="validationWorkflowStack" />
		</action>
	</package>
</xwork>

FormProcessingAction.java:

package lesson04_01_01;

import com.opensymphony.xwork.ActionSupport;

public class FormProcessingAction extends ActionSupport {
	private String checkbox;
	private String file;
	private String hidden;
	private String password;
	private String radio;
	private String select;
	private String textarea;
	private String textfield;
	
	public String getCheckbox() { return checkbox; }
	public String getFile() { return file; }
	public String getHidden() { return hidden; }
	public String getPassword() { return password; }
	public String getRadio() { return radio; }
	public String getSelect() { return select; }
	public String getTextarea() { return textarea; }
	public String getTextfield() { return textfield; }
	
	public void setCheckbox(String checkbox) { this.checkbox = checkbox; }
	public void setFile(String file) { this.file = file; }
	public void setHidden(String hidden) { this.hidden = hidden; }
	public void setPassword(String password) { this.password = password; }
	public void setRadio(String radio) { this.radio = radio; }
	public void setSelect(String select) { this.select = select; }
	public void setTextarea(String textarea) { this.textarea = textarea; }
	public void setTextfield(String textfield) { this.textfield = textfield; }
	
	public String execute() throws Exception {
		return SUCCESS;
	}
}

FormProcessingAction-validation.xml:

<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN"
"http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">

<validators>
  <field name="checkbox">
    <field-validator type="requiredstring">
      <message>Please, check the checkbox.</message>
    </field-validator>
  </field>

  <field name="file">
    <field-validator type="requiredstring">
      <message>Please select a file.</message>
    </field-validator>
  </field>

  <field name="password">
    <field-validator type="requiredstring">
      <message>Please type something in the password field.</message>
    </field-validator>
  </field>

  <field name="radio">
    <field-validator type="requiredstring">
      <message>Please select a radio button.</message>
    </field-validator>
  </field>

  <field name="select">
    <field-validator type="requiredstring">
      <message>Please select an option from the list.</message>
    </field-validator>
  </field>

  <field name="textarea">
    <field-validator type="requiredstring">
      <message>Please type something in the text area.</message>
    </field-validator>
  </field>

  <field name="textfield">
    <field-validator type="requiredstring">
      <message>Please type something in the text field.</message>
    </field-validator>
  </field>
</validators>

ex01-success.jsp:

<%@ taglib uri="webwork" prefix="ww" %>
<html>
<head>
<title>WebWork Tutorial - Lesson 4.1.1 - Example 1</title>
</head>

<body>

<p>UI Form Tags Example result:</p>

<ul>
  <li>checkbox: <ww:property value="checkbox" /></li>
  <li>file: <ww:property value="file" /></li>
  <li>hidden: <ww:property value="hidden" /></li>
  <li>password: <ww:property value="password" /></li>
  <li>radio: <ww:property value="radio" /></li>
  <li>select: <ww:property value="select" /></li>
  <li>textarea: <ww:property value="textarea" /></li>
  <li>textfield: <ww:property value="textfield" /></li>
</ul>

</body>
</html>

Notice how much cleaner ex01-index.jsp is, compared to its HTML result. The default layout of the form components is a table layout, with the label on the left column and the field to the right. You can learn how to create your own layouts when we explain the template system, below.

Another thing to notice is the reference to the validationWorkflowStack in the action's configuration. This makes WebWork validate the parameters that are sent to our actions according to a configuration file we place in the same location as the action class – in our case, FormProcessingAction-validation.xml (see Validation). In case something is not valid, it prevents the action from executing and dispatches the request to the input result with error messages attached to each field (using the method addFieldError(String fieldName, String errorMessage)).

But don't worry about how the validation framework works for now. Run the example and try leaving some fields blank. You will see that the UI tags provide error messages that integrate with the validation framework and that's what we want do demonstrate here. This separation of concerns can help programmers and designers concentrate more on their part of the work.

Try the example!

Other UI Controls:

Besides the standard form controls that HTML designers are already familiar with, WebWork provides some other controls and also the ability to create a custom control. Let's take a look at the custom controls that are already provided by WebWork:

<ww:checkboxlist /> Works just like the <ww:radio /> tag, but with check boxes instead of radio buttons. It gets the keys and values from a collection and creates a list of checkboxes, all with the same name.
<ww:combobox /> Simulates a combo box, which is a control that mixes a selection list with a text field. It does this by placing a text field with a <select /> list right below it and a JavaScript code that fills the text field with the selection of the list every time it changes.
<ww:tabbedpane /> Help needed here.
<ww:token /> Help needed here.

The Template System:

WebWork uses the Velocity template system to render the actual HTML output for all UI tags. A default implementation of all templates has been included with the core distribution allowing users to use WebWork's UI tags "out of the box". Templates can be edited individually or replaced entirely allowing for complete customization of the resulting HTML output. In addition, the default template can be overridden on a per tag basis allowing for a very fine level of control. The default templates are located in the webwork-2.1.1.jar file under /template/xhtml.

If you unpack webwork-2.1.1.jar and look under the /template/xhtml directory you will see a bunch of velocity templates. Most of them correspond to a specific UI Tag, and those have the name of the tag they render. If you're familiar with Velocity, I recommend you analyse the template files to see what you're capable of doing with them. Since version 2.1, there's also a /template/simple directory, which is a simpler version of the HTML form controls (just the control, no table or label).

If you want do display your UI components in a different layout than the one that comes with WebWork, you can:

  • Edit and replace the files in /template/xhtml (repack the JAR or create the same directory structure somewhere else and make sure your container looks that path before the JAR);
  • Change the location of the templates by editing the webwork.ui.theme property in webwork.properties (file that should be placed in the root of your classpath);
  • Specifying the location of the templates for each tag individually using the theme or the template property. The former allows you to specify the directory where all templates are (thus, WebWork looks for templates with the same name as the ones in /template/xhtml), while the latter allows you to indicate the exact template to be used for that component.

Read more: Themes and Templates

The third approach is demonstrated in the example below. Note that, by default, the specified theme directory should be under /template and the specified template file should be under /template/xhtml.

ex02.jsp:

<%@ taglib uri="webwork" prefix="ww" %> 
<html> 
<head> 
<title>WebWork Tutorial - Lesson 4.1.1 - Example 2</title> 
</head> 

<body> 

<p>Template Change Example:</p> 

<p><ww:checkbox name="'checkbox'" label="'A checkbox'" fieldValue="'checkbox_value'" theme="'mytheme'" /></p> 

<p><ww:textfield name="'textfield'" label="'A text field'" template="mytextfield.vm" /></p> 

</body> 
</html>

/template/mytheme/checkbox.vm:

<div align="center"> 
	<input type="checkbox" 
		name="$!webwork.htmlEncode($parameters.name)" 
		value="$!webwork.htmlEncode($parameters.fieldValue)" 
	#if ($parameters.nameValue) checked="checked" #end 
	#if ($parameters.disabled == true) disabled="disabled" #end 
	#if ($parameters.tabindex) tabindex="$!webwork.htmlEncode($parameters.tabindex)" #end 
	#if ($parameters.onchange) onchange="$!webwork.htmlEncode($parameters.onchange)" #end 
	#if ($parameters.id) id="$!webwork.htmlEncode($parameters.id)" #end 
	/><br /> 
	$!webwork.htmlEncode($parameters.label) 
</div>

/template/xhtml/mytextfield.vm:

<div align="center"> 
	<input type="text" 
		name="$!webwork.htmlEncode($parameters.name)" 
	#if ($parameters.size) size="$!webwork.htmlEncode($parameters.size)" #end 
	#if ($parameters.maxlength) maxlength="$!webwork.htmlEncode($parameters.maxlength)" #end 
	#if ($parameters.nameValue) value="$!webwork.htmlEncode($parameters.nameValue)" #end 
	#if ($parameters.disabled == true) disabled="disabled" #end 
	#if ($parameters.readonly) readonly="readonly" #end 
	#if ($parameters.onkeyup) onkeyup="$!webwork.htmlEncode($parameters.onkeyup)" #end 
	#if ($parameters.tabindex) tabindex="$!webwork.htmlEncode($parameters.tabindex)" #end 
	#if ($parameters.onchange) onchange="$!webwork.htmlEncode($parameters.onchange)" #end 
	#if ($parameters.id) id="$!webwork.htmlEncode($parameters.id)" #end 
	/><br /> 
	$!webwork.htmlEncode($parameters.label) 
</div>

HTML result after processing ex02.jsp:

<html> 
<head> 
<title>WebWork Tutorial - Lesson 4.1.1 - Example 2</title> 
</head> 

<body> 

<p>Template Change Example:</p> 

<p><div align="center"> 
  <input type="checkbox" 
         name="checkbox" 
         value="checkbox_value" 
            /><br /> 
  A checkbox 
</div></p> 

<p><div align="center"> 
  <input type="text" 
                                     name="textfield" 
                    /><br /> 
  A text field 
</div></p> 

</body> 
</html>

Try the example!

Building Customized UI Components:

There are some situations in which none of the UI Components that come bundled with WebWork fit your requirements. In this case, the recommended approach would be to create your own custom component. In this way, you keep your web page clean of layout and error-checking issues and also promote component reuse.

To create a custom component, just create a Velocity template for it, just like the ones that already exist. To place it in a web page, use the <ww:component /> tag and specify the location of the template in its template parameter.

To pass parameters to be used by your template, use the <ww:param /> tag (see lesson 4.1). The example below demonstrates the creation of a custom date field.

ex03.jsp:

<%@ taglib uri="webwork" prefix="ww" %> 
<html> 
<head> 
<title>WebWork Tutorial - Lesson 4.1.1 - Example 3</title> 
</head> 

<body> 
<p>Custom Component Example:</p> 

<p> 
<ww:component template="datefield.vm"> 
	<ww:param name="'label'" value="'Date'" /> 
	<ww:param name="'name'" value="'mydatefield'" /> 
	<ww:param name="'size'" value="3" /> 
</ww:component> 
</p> 

</body> 
</html>

/template/xhtml/datefield.vm:

#set ($name = $parameters.get('name')) 
#set ($size = $parameters.get('size')) 
#set ($yearSize = $size * 2) 

$parameters.get('label'): 
<input type="text" name="${name}.day" size="$size" /> / 
<input type="text" name="${name}.month" size="$size" /> / 
<input type="text" name="${name}.year" size="$yearSize" /> (dd/mm/yyyy)

HTML result after processing ex03.jsp:

<html> 
<head> 
<title>WebWork Tutorial - Lesson 4.1.1 - Example 3</title> 
</head> 
<body> 
<p>Custom Component Example:</p> 

<p> 
Date: 
<input type="text" name="mydatefield.day" size="3" /> / 
<input type="text" name="mydatefield.month" size="3" /> / 
<input type="text" name="mydatefield.year" size="6" /> (dd/mm/yyyy) 
</p> 

</body> 
</html>

Try the example!


Previous Lesson | Next Lesson