Vita Rara: A Life Uncommon

Creating a UI Component in Struts 2


Categories: | |

Problem

You have a form "element" that consists of several HTML actual elements. You'd like to create these using one Struts 2 UI tag, rather then write the HTML out everywhere it's needed.

Background

The Struts 2 UI tag library provide a component tag, which can be used to create your own "tags" without needing to dive into the JSP tag library API. Creating a tag using the Struts 2 component tag, also allows it to directly take advantage of the Struts 2 framework, such as OGNL support, and the existing Struts 2 UI tag themes.

Recipe

In this recipe we will creating an HTML control using the component tag that will display two input fields. This is a simple example, but it will show the process of creating a component, and tying it into the existing Struts 2 Xhtml theme.

For our recipe we will pretend that we are collecting a US zip+4 zip code, using a separate input for the two components of the zip code. Using a component to do this will allow our double input "field" to coexist with the Xhtml theme and use the automatic layout provided by the theme.

Unless we created a component to handle this situation, of needing two HTML input elements to be treated as one "component" by the Struts 2 tags, we would have to resort to using the Struts 2 Simple theme and writing our own HTML.


<s:textfield label="City" name="city" />
<s:textfield label="State" name="state" />
<tr>
	<td>Zip+4</td> <!-- Manually outputting our label -->
	<td>
		<s:textfield name="zip" theme="simple" />
		<s:textfield name="plus4" theme="simple" />
	</td>
</tr>

Although this is serviceable, it would be better if we could allow the Struts 2 tags to handle our layout. It simplifies form creation and allows uniform styling to be automatically applied. Also, should a change happen in how zip codes are collected, we would have to revisit every form that has this HTML construct on it, and change it. If we were to replace this with a component, all of our markup for zip+4 would be contained in one place in our code, and could be changed simply by changing our component template.

The Struts 2 UI Xhtml Theme

We will start with the standard Struts 2 Xhtml theme. To control the layout of the table elements that wrap our form elements the Xhtml theme uses two files, controlheader.ftl and controlfooter.ftl. All of the logic for the table elements, arrangement of labels, and presentation of field errors is located in these files. We don't want to re-create these so we will use them as the basis for our component.

The Xhtml Theme text.ftl Template

To create our component we will start with the text.ftl template from the Xhtml theme.


1: <#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
2: <#include "/${parameters.templateDir}/simple/text.ftl" />
3: <#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

First let's examine this template:

  • On line 1 it includes the controlheader.ftl template, which handles starting our table row, and displaying our label in a td.
  • On line 2 it includes the simple theme's text.ftl template.
  • On line 2 it includes the controlfooter.ftl template to handle closing the td and tr surrounding out input element.

As you can see the theme template uses many common templates to create their output and to extend other themes. Throughout the Xhtml theme you will see it including template files from the simple theme to create the actual input element. This allows the Xhtml theme to concentrate on the layout of the form in the table, and the presentation of errors.

Our Component Template

We start with the text.ftl template and simply replace the second line with the content we want. In our case two textfield's. We use the existing Struts 2 textfield UI tag to do this.


<#include "/${parameters.templateDir}/${parameters.theme}/controlheader.ftl" />
<@s.textfield theme="simple" name="${parameters.zipName?default('')}" 
                 value="${parameters.zipValue?default('')}" /> + 
<@s.textfield theme="simple" name="${parameters.plus4Name?default('')}" 
                 value="${parameters.plus4Value?default('')}" />
<#include "/${parameters.templateDir}/xhtml/controlfooter.ftl" />

Using the existing Struts 2 textfield allows us to leverage the existing tags. This allows a markup to remain highly consistent.

Accessing the information passed into our component is done using the parameters variable. Our component will take four values:

  • zipName: the name to be applied to the input tag. This needs to be the OGNL expression that will set the value back onto your action/model when it is submitted.
  • zipValue: the value of the zip code from your action/model.
  • plus4Name: the name to be applied to the input tag for the plus 4.
  • plus4Value: the value of your plus 4.

Let's revisit our example from earlier and use our new tag:


    <s:textfield name="" label="City" />
    <s:textfield name="" label="State" />
    <s:component template="/components/zipplus4field.ftl" label="Zip+4">
        <s:param name="zipName" value="%{'zip'}" />
        <s:param name="zipValue" value="%{zip}" />
        <s:param name="plus4Name" value="%{'plus4'}" />
        <s:param name="plus4Value" value="%{plus4}" />
    </s:component>

All of the boilerplate HTML code is gone. Instead we are left with very clean Struts 2 tags.

Discussion

Using the Struts 2 UI tags and custom components you can encapsulate repetitive HTML patterns. Keeping your HTML mark up isolated in your component templates keeps it all in one place. Should you need to change it you will only have to do this in one place. Also, using UI components and extending the existing themes allows your HTML markup to participate in the Struts 2 themes, freeing you from laying out your forms in tables, or using other layout techniques.

To see how it all fits together download the example project.

AttachmentSize
struts2component.tar_.gz8.98 KB

How do i increase the column width

Hi Mark,

I really liked your theme. What I am trying to do is I am trying to create column which extends across multiple rows. Is that possible using the qxhtml theme.

nested components

Hi,

I am trying to create a custombuttonbar component which can house one or more nested custombutton components. I have created three ftls,

  • custombuttonbar.ftl
  • custombuttonbar-close.ftl
  • custombutton.ftl

my code looks something like this... (skipping the unnecessary details)

<s:component template="custombuttonbar.ftl">
    <s:component template="custombutton.ftl"/>
</s:component>

but with this, the ftls are firing in a weird order. custombutton.ftl fires first. then custombuttonbar.ftl and the custombuttonbar-close.ftl does not fire at all! So i have temporarily written this in the ugly form

<s:component template="custombuttonbar.ftl"/>
<s:component template="custombutton.ftl"/>
<s:component template="custombuttonbar-close.ftl"/>

Hope someone can help me solve this problem..

nested components

Hi,

I had exactly the same need as you. I found nothing consistent on nested content for Struts UI tags. It's a shame. So I used the "param" tag to embed the nested content, like this :

button-bar.ftl :

<div class="button-bar">
  <div class="buttonBarContainer">
    ${parameters.body?if_exists?html}
  </div>
</div>

example.jsp :

<s:component template="button-bar.ftl">
  <s:param name="body">
    <s:submit name="create" type="button" label="commons.action.create" />
  </s:param>
</s:component>

may be this way can solve

may be this way can solve your problem.
${parameters.body?string}

Parameters

Hi,

I have followed you're example in an attempt to have two datetimepicker(s) side by side, I get everything to work except that using s:param in my jsp does not seem to pass the parameters to the ftl file. I have tried you're example on my environment it does not work either.

Do you have any ideas

environment
-----------
jboss 4.2.2
struts 2.0.9
java 5.11ish

Cheers Alan.

DateTimePicker

Hi Mark,

I.m trying to do the same sort of thing using the struts datetimepicker tag. However I receive a freemarker error,

s.datetimepicker not found

Is it possible to do this using any struts tag? I am missing something simple?

Regards,

Alan.

RE: DateTimePicker

If you are using Struts 2.1.X, you must have the struts2-dojo-plugin-2.1.X.jar in your library. Then, you must put in your page the following:

> > So you need to include ' taglib uri="/struts-dojo-tags" prefix="sx" '
> > and ' sx:head/ '
> >
> > and call sx:datetimepicker .../
> >
> > Michaël

And that's it! I had the same problem, and this guy Michael helped me out.

Cheers bro

--Alfredo

Can not open example project

I download struts2component.tar.gz file, but I have different to open it. How can I open or unzip example project?

Thanks

Eric

Opening Example Project

Try changing the name to struts2component.tar.gz instead of struts2component.tar_.gz. For some reason when I upload the file through my CMS it adds the underscore. Removing the underscore might allow your file type bindings to match.