|
FormDef ManualInstallation
Declaring Forms Customizing form fields
Converters
Nested Beans
FormUtils Combining FormDef and Validator configuration
Installation
cvs -d :pserver:guest@cvs.formdef.dev.java.net:/cvs checkout -P formdef <plug-in className="formdef.plugin.FormDefPlugIn"> <set-property property="pathnames" value="/WEB-INF/form-defs.xml"/> </plug-in>Note: FormDef supports a standalone XML format and a combination XML format which you can use to combine form definitions and form validations. The combination XML format is useful if you use the Validator plugin and would like to keep the definition and validation information in one place. For more information, see Combining FormDef and Validator configuration. Declaring Forms
<form-definition> <formset> <form name="myForm" beanType="com.my.dto.MyBean"/> </formset> </form-definition>When the plugin gets executed, it will create a DynaActionForm containing String fields for each property of MyBean. The JavaBeans specification is used to identify the fields that MyBean has. For fields that do not follow the JavaBeans specs, see Specifying field accessor methods. <form-definition> <formset> <form name="myForm" beanType="com.my.dto.MyBean" formType="org.apache.struts.validator.DynaValidatorForm"/> </formset> </form-definition>To change the default type used for all form beans, add a formdef-config element: <form-definition> <formdef-config formType="org.apache.struts.validator.DynaValidatorForm"/> <formset> ... </formset> <form-definition>The global declaration changes the default type. Individual form beans can still specify a different formType as needed. FormUtils.getFormValues() to convert the form bean
to a business object, the default behavior is to use the business object's
no-arg constructor. This behavior can be changed by specifying a class that
will act as a factory for your business object:
<form name="personForm" beanType="com.my.dto.Person"> <factory type="com.my.dto.PersonFactory" method="getPerson" nameParam="true"/> ... <form/>For the above example, when FormUtils needs an instance of personForm, it will call the getPerson method of the PersonFactory class and pass it the name of form bean as the parameter. Note that PersonFactory doesn't need to implement any interface and the actual method can be given any name. The factory method may or may not accept parameters, and in the case where it does, it has to be a single String identifying the name of the form bean to be instantiated. The return value must be an instance of the business object for that form bean. Customizing form fields
<form name="sampleForm" beanType="com.my.dto.SampleBean"> <field property="specialDate" getter="getLastSpecialDate" setter="addSpecialDate"/> </form>In the above example, a specialDate field is added to the form
bean declaration. During formatting and parsing operations, the getLastSpecialDate
method is called to retrieve the current specialDate, and addSpecialDate is
called to pass the value from the form bean to the business object.
<form name="sampleForm" beanType="com.my.dto.SampleBean"> <field property="selectedButton"/> </form>The above example will add a selectedButton field in the sampleForm form bean, along with the fields from SampleBean .
java.util.String by default.
This can be overridden by providing a type attribute to a field.
<form name="memberForm" beanType="com.my.dto.Member"> <field property="inactive" type="java.lang.Boolean"/> </form> <form name="memberForm" beanType="com.my.dto.Member"> <excludes>createDate, updateDate</excludes> </form>In the example above, the createDate and
updateDate fields of Member will not
have corresponding fields on memberForm . Fields
listed in an <excludes> element will not be included on the
form even when there is a <field> element configured for it.
Optionally, a field can be configured for exclusion using the
exclude attribute of <field> element.
<form name="memberForm" beanType="com.my.dto.Member"> <field property="createDate" exclude="true"/> <field property="updateDate" exclude="true"/> </form> Converters
<field property="doubleField"> <converter param="###,###,##0.000"/> </field> <field property="dateField"> <converter param="yyyy-MM-dd"/> </field>Numeric data can be handled without specifying a format String. Date fields, on the other hand, require format Strings and one should always be provided when a form contains any type of Date object. Global converters can be used to specify field formats that can be used for all forms. form-defs.xml file.
Instead of specifying the param attribute, a key
attribute can be given instead. Message format strings can also be
placed in a separate resource bundle. These formats can be specified
by using the bundle attribute.
<field property="doubleField"> <converter key="currencyFormat"/> </field> <field property="dateField"> <converter key="dateFormat" bundle="bundle2"/> </field>In the example above, the currencyFormat key is used to
find the format to be applied for the doubleField property of the form.
For the dateField property, the dateFormat key is used
on the the bundle2 resource bundle.
Specifying conversion formats this way allows an application supporting
multiple locales to adjust the data format used for each locale.
There are three types of global converters that can be declared: property type converters, property name converters, and named converters. Each global converter declaration can specify either a conversion format to apply, a converter class to use, or both. <form-definition> <global-converters> <global-converter for="property-type" target="java.util.Date" param="MM/dd/yyyy"/> </global-converters> </form-definition>With the above declaration, FormDef would use the "MM/dd/yyyy" string to format and parse all Date fields. Another use would be to declare a conversion class to use for a custom field type: <global-converter for="property-type" target="formdef.plugin.example.employee.business.EmploymentCode" type="formdef.plugin.example.employee.web.EmploymentCodeConverter"/>The above declaration would use the EmploymentCodeConverter class to convert all fields that have the EmploymentCode type. <global-converter for="converter-name" target="salary" param="###,###,##0.00"/>The above declaration would format all salary fields in all forms using
the ###,###,##0.00 format string.
<global-converter for="converter-name" target="salaryConverter" param="###,###,##0.00"/>The above global-converter entry declares a converter configuration with the name "salaryConverter". This converter can be associated with fields in form declarations: <form name="sampleForm" beanType="com.my.dto.SampleBean"> <field property="desiredSalary"> <converter name="salaryConverter"/> </field> </form> Nested Beans
<!-- 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>Internally, a special type of converter, FormConverter ,
is associated with the field, and uses the form name as conversion
parameter to find the FormDef mapping.
<!-- here's our configuration for the Address bean --> <form name="addressForm" beanType="my.package.Address"/> <-- (1) --> <form name="employeeForm" beanType="my.package.Employee"> <!-- specify that our address field should use ArrayList --> <field property="address" type="java.util.ArrayList" <-- (2) --> formName="addressForm"> <-- (3) --> <converter type="formdef.plugin.conversion.FormCollectionConverter"/> <-- (4) --> </field> </form> FormCollectionConverter
will loop through the collection of the bean and create their
counterpart forms, and vice versa, during conversion.
When creating the collection for either the bean or the form,
FormCollectionConverter will use the declared type
for the field to instantiate a Collection. In the example above,
the "address" Collection will be set to an instance of
"java.util.ArrayList". Similarly, on the
my.package.Employee bean, its "address" Collection
will be set to an instance of its declared type.
If the declared types on either the form or the bean are abstract
classes, such as java.util.List or java.util.Set, additional
properties can be configured to specify which implementation
class will be used for the field.
<form name="employeeForm" beanType="my.package.Employee"> <field property="address" type="java.util.List"> <converter type="formdef.plugin.conversion.FormCollectionConverter" param="addressForm"> <set-property key="formType" value="java.util.ArrayList/> <set-property key="beanType" value="java.util.ArrayList/> </converter> </field> </form> In the example above, "address" is declared as the abstract class "java.util.List", with the "formType" converter property is used to tell FormCollectionConverter which List implementation to instantiate. Likewise, the FormUtils
FormUtils.setFormValues() can be used to initialize a form bean
with values from a business object:
DynaActionForm dynaForm = (DynaActionForm) FormDefUtil.setFormValues("employeeForm", employee, this, mapping, request);The first parameter identifies the form definition that will be used. The second parameter is the business object which contains the values that will be used to populate the form bean. The last three parameters are the current Action object, the current action mapping, and the request being processed. FormUtils.setFormValues() returns the populated form bean.
This can then be placed by the caller in the proper scope with the proper
name so that Struts can find it when rendering an HTML form.
FormUtils.getFormValues() can be used to create a populated business object
from a form bean:
Employee employee = (Employee) FormDefUtil.getFormValues(form, this, mapping, request);The first parameter is the form passed by Struts to the action object. The next three parameters are the current Action object, the current action mapping, and the request being processed. getFormValues() returns the business object populated with
values from the form bean. If a factory is associated with the form
definition, it is called to create the business object that will be populated.
Otherwise, the business object's no-arg constructor will be used to create
the object.
setFormUtils() and getFormUtils . These can be
used for more control in the formatting and parsing of form data.
Consult the FormUtils class for more details.
setFormUtils() and
getFormUtils are implemented by instance methods
that can be overridden. To accomplish this, a FormUtils
subclass can be provided with the overriding method implementations.
A FormUtilsFactory implementation must be provided to return
the FormUtils subclass that should be used.
The register the custom FormUtilsFactory implementation,
the formUtilsFactory attribute of the
formdef-config element is provided:
<form-definition> <formdef-config formUtilsFactory ="com.my.FormUtilsFactory"/> ... <form-definition>Note: Experience has shown that changes to the Struts internals can have significant impact on the non-static methods of FormUtils. While these methods can be overridden, be aware that they are subject to change to conform to changes within Struts itself. A post-1.0 version is certain to change to support the Struts 1.3 ActionContext interface.
Combining FormDef and Validator configuration
<form-definition> <formdef-config formType="org.apache.struts.validator.DynaValidatorForm"/> <global-converters> <global-converter for="property-type" target="java.util.Date" param="MM/dd/yyyy"/> </global-converters> <formset> <form name="employeeForm" beanType="formdef.plugin.example.employee.business.Employee"> <field property="salary"> <converter param="###,###,##0.000"/> </field> </form> </formset> </form-definition>The above example declares that the DynaValidatorForm should be used as the default form type. It also declares a global format string for Date fields, and declares an employeeForm with a format string for the
salary field.
Here's the validation config for the form:
<form-validation> <form name="employeeForm"> <field property="birth" depends="required,date"> <arg0 key="employeeform.birth.displayname"/> <var> <var-name>datePatternStrict</var-name> <var-value>MM/dd/yyyy</var-value> </var> </field> <field property="salary" depends="required,mask"> <arg0 key="employeeform.salary.displayname"/> <var> <var-name>mask</var-name> <var-value>^(?:\d+|\d{1,3}(?:,\d{3})*)(?:\.\d{1,2}){0,1}$</var-value> </var> </field> </form> </form-validation>The above config validates that the birth and salary
fields have the right format. Combining the two configurations, we have:
<form-definition> <formdef-config formType="org.apache.struts.validator.DynaValidatorForm"/> <global-converters> <global-converter for="property-type" target="java.util.Date" param="MM/dd/yyyy"/> </global-converters> <formset> <form name="employeeForm" beanType="formdef.plugin.example.employee.business.Employee"> <field property="birth" depends="required,date"> <arg0 key="employeeform.birth.displayname"/> <var> <var-name>datePatternStrict</var-name> <var-value>MM/dd/yyyy</var-value> </var> </field> <field property="salary" depends="required,mask"> <converter param="###,###,##0.000"/> <arg0 key="employeeform.salary.displayname"/> <var> <var-name>mask</var-name> <var-value>^(?:\d+|\d{1,3}(?:,\d{3})*)(?:\.\d{1,2}){0,1}$</var-value> </var> </field> </form> </formset> </form-definition>The <global-converter> remains as is, and that's because there's no equivalent in Validator. The form declaration now combines the definition and validation information. The <form> has the bean-name attribute for FormDef. The <field>s have the depends attribute for Validator, the <converter> element for FormDef, and the <arg0>, and <var> elements for Validator. Be aware that properties of the Employee class that do not have
validation rules will not be validated but are still declared in the resulting
form bean.
This example is based on the Employees example application.
<plug-in className="formdef.plugin.validator.CombinedFormDefPlugIn"> <set-property property="pathnames" value="/WEB-INF/form-defs-validator-employees.xml"/> </plug-in> <plug-in className="formdef.plugin.validator.CombinedValidatorPlugIn"> <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/form-defs-validator-employees.xml"/> </plug-in>Notice that the CombinedValidatorPlugIn is also passed the validator-rules.xml
which contains the standard declarations that come with the Validator plug-in. It can
accept existing validator.xml files that are using the default Validator DTD as well as
those containing the combined format.
validate=false in a form or field's declaration.
<form name="mySimpleForm" beanType="com.my.dto.MySimpleBean" validate="false"> <field name="someField"/> </form> <form name="myOtherForm" beanType="com.my.dto.MyOtherBean> <field name="someField" depends="required"/> <field name="selectedButton" validate="false"/> </form>In the example given above, mySimpleForm configures a form
bean definition but specifies that it should not be passed to the Validator engine.
A second form, myOtherForm , has one field that requires validation,
someField . Its associated business object may contain several other
fields, and FormDef will create a form bean which contains all those fields.
Because those fields aren't declared here, Validator will not "see" those other fields.
However, an additional field, selectedButton , is being added to the form
bean. To prevent Validator from "seeing" this field, validate=false
is added to its declaration.
generate=false in a form's declaration.
<form name="myExistingForm" generate="false"> ... </form>In the example given above, myExistingForm defines validation rules
for a similarly named form. The generate=false attribute will
tell the CombinedFormDefPlugIn not to generate a form bean for this form,
which could be defined in struts-config.xml or is a ValidatorActionForm
subclass.
|