Nested Beans with FormDef
Note: The code samples shown in this guide uses syntax for FormDef 0.6 for
Struts 1.2.4 or later.
Using FormDef to declare a form bean whose fields map directly to a business
object is straightforward.
Given the following employee object (formatted for brevity):
package my.package;
public class Employee {
private int number;
private String name;
private String address;
private double salary;
public int getNumber() { return number; }
public void setNumber(int number) { this.number = number; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getAddress() { return address; }
public void setAddress(String address) { this.address = address; }
public double getSalary() { return salary; }
public void setSalary(double salary) { this.salary = salary; }
}
and the following configuration:
<form name="employeeForm"
beanType="my.package.Employee">
FormDef will declare a form bean equivalent to the following:
<form-bean name="employeeForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="number" type="java.lang.String"/>
<form-property name="name" type="java.lang.String"/>
<form-property name="address" type="java.lang.String"/>
<form-property name="salary" type="java.lang.String"/>
</form-bean>
All form fields default to the java.lang.String type. The
provided getFormValues() and setFormValues() will
use the included converters to format and parse data between the form bean
and the business object.
In certain cases, it becomes necessary to have a form represent a business
object that is composed of other business objects. FormDef's default behavior
of using Strings for all fields will apply to these as well. To change the
type used for a form field, it should be specified in the field's configuration
when the form is declared.
Let's say that the address property of the Employee business object uses the
following Address class:
package my.package;
public class Address {
private String street;
private String city;
private String state;
private int zip;
public String getStreet() { return street; }
public void setStreet(String street) { this.street = street; }
public String getCity() { return city; }
public void setCity(String city) { this.city = city; }
public String getState() { return state; }
public void setState(String state) { this.state = state; }
public int getZip() { return zip; }
public void setZip(int zip) { this.zip = zip; }
}
Our field in Employee has the usual declarations:
public class Employee {
....
private Address address;
public Address getAddress() { return address; }
public void setAddress(Address address) { this.address = address; }
}
The configuration of the employee form bean can be modified to work with the
nested object. To do this, we declare a form called addressForm
to hold the address fields. On the employeeForm , we will specify
that the address property is associated with a FormDef form by specifying a
formName attribute.
Our FormDef configuration will change to:
<!-- here's our configuration for the Address bean -->
<form name="addressForm"
beanType="my.package.Address"/>
<form name="employeeForm"
beanType="my.package.Employee">
<!-- specify that our address field should use addressForm -->
<field property="address" formName="addressForm"/>
</form>
With addressForm declared, and employeeForm told
about it, FormDef will now use that information to handle the nested form.
It is important to note that when using this technique, the form bean should
be prepopulated and placed in session scope (the default setting in Struts).
This allows Struts to reuse the form definition that was initialized before the
form was created, with the employee field containing the correct
dyna properties.
There's no change in the calls to prepopulate the form and to retrieve its
values.
To prepopulate the form:
// pass the data from the employee bean to the form
ActionForm employeeForm =
FormUtils.setFormValues("employeeForm", employee,
this, mapping, request);
// make the form available to the JSP
request.getSession().setAttribute("employeeForm", employeeForm);
If we want an empty form, we can use the createActionForm() method
in FormUtils:
// create our empty form bean
ActionForm employeeForm = FormUtils.getInstance()
.createActionForm("employeeForm", this, mapping, request);
// pass our form to the JSP
request.getSession().setAttribute("employeeForm", employeeForm);
To retrieve values from the form:
// get the employee values from the submitted form
Employee employee = (Employee)
FormUtils.getFormValues(form, this, mapping, request);
In our JSP, the field names reflect the nesting of the forms:
<html:form action="/saveEmployee">
<html:hidden property="number"/>
<TABLE>
<TR><TD> Employee Number </TD>
<TD> <bean:write name="employeeForm" property="number"/> </TD>
</TR>
<TR><TD> Name </TD>
<TD> <html:text property="name"/> </TD>
</TR>
<TR><TD> Street Address </TD>
<TD> <html:text property="address.street"/> </TD>
</TR>
<TR><TD> City </TD>
<TD> <html:text property="address.city"/> </TD>
</TR>
<TR><TD> State </TD>
<TD> <html:text property="address.state"/> </TD>
</TR>
<TR><TD> Zip Code </TD>
<TD> <html:text property="address.zip"/> </TD>
</TR>
<TR><TD> Salary </TD>
<TD> <html:text property="salary"/> </TD>
</TR>
</TABLE>
</html:form>
To validate the form, we use the Validator plug-in with the following
configuration:
<form name="employeeForm">
<field property="name" depends="required">
<arg0 key="employeeform.name.displayname"/>
</field>
<field property="address.street" depends="required">
<arg0 key="employeeform.address.street.displayname"/>
</field>
<field property="address.city" depends="required">
<arg0 key="employeeform.address.city.displayname"/>
</field>
<field property="address.state" depends="required">
<arg0 key="employeeform.address.state.displayname"/>
</field>
<field property="address.zip" depends="required,integer">
<arg0 key="employeeform.address.zip.displayname"/>
</field>
<field property="salary" depends="required,mask">
<arg0 key="employeeform.salary.displayname"/>
<var>
<var-name>mask</var-name>
<var-value>^\$?\d+(,\d{3})*(\.\d{2})?$</var-value>
</var>
</field>
</form>
|