Form Input HTML
Introduction
The author treats Struts UI tags for form controls as a poor subset of HTML tags, particularly lacking FORM and DATA-* attributes. This article describes how to use form input HTML tags with this library.
An important note is whereas standard Struts design uses the same Action for form processing and display, this
library separates these into different Actions. To display a form field values, the OGNL expression is the form
name and the property name, such as <s:property value="searchForm.id" />. The form input tag
just uses the receiving form field name, such as <INPUT NAME="id">
Also, JSP code access formatted string, form field values, not any converted, non-string value.
Text Fields and Text Areas
The form field value is easily substituted, such as below.
<INPUT CLASS="field" STYLE="width: 200px;" NAME="name" TYPE="TEXT" VALUE="<s:property value="form.name" />" > <TEXTAREA CLASS="field" STYLE="height: 300px; width: 820px;" NAME="message"><s:property value="form.message" /></TEXTAREA>
Checkboxes and Radio Buttons
The CHECKED attribute must be written when appropriate. Checkboxes are for boolean fields and the formatted values are '1' and ''. Fortunately, when OGNL coerces string to boolean, non-empty strings become true. Radio buttons are typically for enumerated type fields and the formatted values are the value name as a string. Thus, the ternary operator can result in 'CHECKED' or not, as shown below.
<INPUT CLASS="field" TYPE="CHECKBOX" NAME="enable" <s:property value="form.enable?'CHECKED':''" /> > <INPUT CLASS="field" TYPE="RADIO" NAME="rgb" VALUE="RED" <s:property value="(testForm.rgb == 'RED')?'CHECKED':''" /> > <INPUT CLASS="field" TYPE="RADIO" NAME="rgb" VALUE="GREEN" <s:property value="(testForm.rgb == 'GREEN')?'CHECKED':''" /> > <INPUT CLASS="field" TYPE="RADIO" NAME="rgb" VALUE="BLUE" <s:property value="(testForm.rgb == 'BLUE')?'CHECKED':''" /> >
Drop-down Lists
The form field to receive the value of a drop-down list is typically a single integer or enumerated type but the viewer Action needs a view helper for construction of the full list. These are typically subclasses of the following Templates.
| Type | Template class | Notes |
|---|---|---|
| Enumerated type | EnumSingleSelectBoxDisplay2<E> | |
| Grouped enumerated type | GroupedEnumSingleSelectBoxDisplay<E> | Groups enumerated values using OPTGROUP. |
| Grouped record list | GroupedSingleSelectBoxDisplay<K,T,G> | Groups records using OPTGROUP. T is usually a database record, K its primary key, and G record or String. |
| Integer list | IntegerListSelectBoxDisplay2 | |
| Record list | SingleSelectBoxDisplay2<K,T> | T is usually a database record and K its primary key. |
The example below is a view helper for countries where the id is the form field value and the name is the display text.
public class CountrySelectBoxDisplay extends SingleSelectBoxDisplay2<Integer,CountryVO> {
@Override
protected Integer getKey(CountryVO item) {
return item.getId();
}
@Override
protected String getText(CountryVO item) {
return item.getName();
}
@Override
protected String getValue(Integer key) {
return Integer.toString(key);
}
@Override
protected boolean hasBlankValue() {
return true;
}
}
Below is the relevant lines from the viewer Action.
@Form
private AddVatRateForm form;
private CountrySelectBoxDisplay operatorCountrySelectBoxDisplay;
...
operatorCountrySelectBoxDisplay = new CountrySelectBoxDisplay();
operatorCountrySelectBoxDisplay.setModel(getCountries());
operatorCountrySelectBoxDisplay.setSelectedValue(form.getOperatorCountryId());
Below creates the HTML tags for the drop-down list.
<SELECT CLASS="field" STYLE="width: 140px;" NAME="operatorCountryId">
<s:iterator var="operatorCountry" value="operatorCountrySelectBoxDisplay.list" status="loop">
<OPTION <s:property value='#operatorCountry.selectedAttribute' /> VALUE="<s:property value='#operatorCountry.value' />"
><s:property value='#operatorCountry.text' /></OPTION>
</s:iterator>
</SELECT>
Longer drop-down lists split into groups are also supported. For example, below, services may refer to their named group, groups have a display order, and services have a display order within their group.
public class ServiceSelectBoxDisplay extends GroupedSingleSelectBoxDisplay<Integer,ServiceDTO,ServiceGroupDTO> {
protected String getBlankText() {
return "-- please select --";
}
@Override
protected String getValue(ServiceDTO item) {
return Integer.toString(item.getId());
}
@Override
protected String getText(ServiceDTO item) {
return item.getName();
}
@Override
protected ServiceGroupDTO getGroup(ServiceDTO item) {
return item.getServiceGroup();
}
@Override
protected String getGroupLabel(ServiceGroupDTO group) {
return (group != null)?group.getName():"(Ungrouped)";
}
@Override
public Integer getKey(ServiceDTO item) {
return item.getId();
}
protected Comparator<SelectBoxGroupDisplay<Integer,ServiceDTO,ServiceGroupDTO>> getGroupSortComparator() {
return (o1,o2) -> ((o1.getData() != null)?o1.getData().getDisplayOrder():0) - ((o2.getData() != null)?o2.getData().getDisplayOrder():0);
}
protected Comparator<SelectBoxItemDisplay2<Integer,ServiceDTO>> getSortComparator() {
return (o1,o2) -> (o1.getData().getDisplayOrder() - o2.getData().getDisplayOrder());
}
}
The HTML tags are created by looping through each group, then each member within.
<SELECT SIZE="1" NAME="serviceId">
<s:iterator value="serviceSelectBoxDisplay.list" var="serviceGroupDisplay">
<OPTGROUP LABEL="<s:property value="#serviceGroupDisplay.text"/>">
<s:iterator value="#serviceGroupDisplay.children" var="serviceDisplay">
<OPTION VALUE="<s:property value="#serviceDisplay.value"/>" <s:property value="#serviceDisplay.selectedAttribute"/>
><s:property value="#serviceDisplay.text"/></OPTION>
</s:iterator>
</OPTGROUP>
</s:iterator>
</SELECT>
Multiple Selection Lists
SELECT HTML tags using MULTIPLE generate multiple parameters of the same name. Unlike standard Struts, multiple parameters with the same name aren't converted to other data types but a String array on a form will be set. The formatted values can then be manually converted, such as below.
private String[] serviceIds;
private Collection<Integer> parsedServiceIds;
...
public void doValidate2(ValidationAware2 validationAware, TextProvider textProvider) {
parsedOtherServiceIds = new ArrayList<>();
for (String serviceId: serviceIds) {
parsedServiceIds.add(Integer.parseInt(serviceId));
}
}
Like the single drop-down list, create a view helper from the MultiSelectBoxDisplay<K,T> Template class.
public class ServiceMultiSelectBoxDisplay extends MultiSelectBoxDisplay<Integer,ServiceDTO> {
@Override
protected Integer getKey(ServiceDTO item) {
return item.getId();
}
@Override
protected String getText(ServiceDTO item) {
return item.getName();
}
@Override
protected String getValue(ServiceDTO item) {
return Integer.toString(item.getId());
}
}
Below is the relevant lines from the viewer Action.
serviceMultiSelectBoxDisplay = new ServiceMultiSelectBoxDisplay();
serviceMultiSelectBoxDisplay.setModel(getServices());
serviceMultiSelectBoxDisplay.setSelectedValues(form.getParsedServiceIds());