blob: 8726e9b00fc5807f26687bbf3d9c84ffe7007315 [file] [log] [blame]
---
Using the BeanEditForm Component
---
Using the BeanEditForm Component
Tapestry includes a powerful component capable of generating a complete create/edit user interface for a typical JavaBean, BeanEditForm.
BeanEditForm analyzes the the properties of the bean, locating just those properties that are readable and writable. It filters down
to properties whose type is mapped to a known editor (this is described in more detail below).
The default ordering for properties is in the order in which the <getter methods> for the properties are defined.
When a super-class defines edittable properties, those are ordered before sub-class properties.
* Supported Types
The default set of property types supported by BeanEditForm:
* String: as a text field
* Number: as a text field
* Enum: as a drop-down list
* Boolean: as a checkbox
[]
Resolving a property type to an editor type involves a search up the inheritance hierarchy: thus the super-type of Integer, Long, BigDecimal, etc. is
Number, which uses a text field for data entry.
The list of supported property types is extensible (this is documented below).
* Automatic Object Creation
When a page is rendered, the BeanEditForm component will read its object parameter as the JavaBean to edit (with the current properties
of the JavaBean becoming the defaults for the various fields). Likewise, when the form is submitted by the user, the object parameter
is read and its properties populated from the request.
If the object does not exist, it will be created as needed. The type is determined from the property type, which should be a specific type
in order for automatic creation to operate properly.
* Implicit Object Binding
If the object parameter is not bound, then an implicit binding to a property of the containing component is made. The bound property will be
the BeanEditForm component's id, if such a property exists. Thus you may typically give the BeanEditForm component an id (that matches a property)
and not have to bind the object parameter.
* Non-Visual Properties
In some cases, a property may be updatable and of a supported type for editing, but should not be presented to the user for editing: for example,
a property that holds the primary key of a database entity. In such a case, the
{{{../../apidocs/org/apache/tapestry5/beaneditor/NonVisual.html}NonVisual}} annotation may be applied to the property (either the getter
or the setter method).
* Default Validation
Default validation for fields is primary determined by property type.
If desired, additional validation may be specified using the
{{{../../apidocs/org/apache/tapestry5/beaneditor/Validate.html}Validate}} annotation.
* Property ordering
By default, the order in which properties are presented is as defined above (order of the getter method).
The {{{../../apidocs/org/apache/tapestry5/beaneditor/Order.html}Order}} annotation may be used to modify the normal ordering.
* Default Label
Tapestry will attempt to provide a reasonable default label for each field, based on the property name being emitted. The property name
is capitalized, and spaces are added before case changes, thus property "name" becomes label "Name" and property "streetAddress" becomes label
"Street Address".
BeanEditForm also searches for a label for the field in the containing component's message catalog. The message key is the property name suffixed with "-label".
If such a label is found, it takes precedence.
Property Editor Overrides
You may override the editor for any particular property, using the a block parameter to the BeanEditForm component.
An editor normally consists of a Label component and some form of field component (such as TextField or TextArea).
For example, you may want to selectively use a PasswordField component:
+---+
<t:beaneditform object="loginCredentials">
<t:parameter name="password">
<t:label for="password"/>
<t:passwordfield t:id="password" value="loginCredentials.password"/>
</t:parameter>
</t:beaneditform>
+---+
The other fields will render normally (using the built-in editors).
Customizing the BeanModel
You may want to customize the BeanModel further, to remove from the form properties that should not be editable by the user,
and to change the order in which properties are presented within the form.
The BeanEditForm component has two parameters for this purpose:
* remove: A comma separated list of property names to remove from the model.
* reorder: A comma separated list of property names indicating the desired order.
[]
If a model has more properties that are listed in the reorder parameter, then the additional properties will be ordered at the end of the form.
Note that these parameters <modify> the BeanModel.
Providing the BeanModel
The BeanEditForm component operates in terms of a {{{../../apidocs/org/apache/tapestry5/beaneditor/BeanModel.html}BeanModel}}, which describes
the properties, their presentation order, labels and so forth.
Normally, the BeanEditForm automatically creates the BeanModel as needed, based on the type of object bound to its object parameter.
Alternately, the BeanModel can be supplied as the model parameter. This can be useful in situations where the remove and reorder parameters
are insufficient. For example, if the the type of the property being edited is an interface type, it may be useful to provide
an explicit BeanModel around an underlying implementation class.
The model can be created when the page is first instantiated:
+---+
public class MyPage
{
@Inject
private BeanModelSource beanModelSource;
@Inject
private ComponentResources resources;
@Retain
private BeanModel model;
@Property
private MyBean bean;
{
model = beanModelSource.create(MyBean.class, true, resources);
// Make other changes to model here.
}
public BeanModel getModel() { return model; }
}
+---+
And, in the component template, the built model can be passed to the BeanEditForm component explicitly:
+---+
<t:beaneditform object="bean" model="model"/>
+--+
Adding New Property Editors
Adding a new property editor is a three step process.
First, decide on a logical name for the data type. For example, you may decide that the BigDecimal type will represent currency in your application, so name the data type "currency".
Next, you must make contributions to the
{{{../../apidocs/org/apache/tapestry5/services/DataTypeAnalyzer.html}DataTypeAnalyzer}} or
{{{../../apidocs/org/apache/tapestry5/services/DefaultDataTypeAnalyzer.html}DefaultDataTypeAnalyzer}} services to match properties to your new name.
DataTypeAnalyzer is a chain of command that can make match properties to data types based on property type or annotations on the property. In general,
DefaultDataTypeAnalyzer is used, as that only needs to consider property type. DefaultDataTypeAnalyzer matches property types to data types, based on a search up
the inheritance path.
+---+
public static void contributeDefaultDataTypeAnalyzer(MappedConfiguration<Class, String> configuration)
{
configuration.add(BigDecimal.class, "currency");
}
+---+
You must provide an editor for the "currency" data type. An editor is a block of a page of the application; this page is not normally rendered itself, but acts as a container for
one or more blocks.
+---+
public class AppPropertyEditBlocks
{
@Environmental
private PropertyEditContext context;
@Component(parameters =
{ "value=context.propertyValue", "label=prop:context.label",
"translate=prop:currencyTranslator", "validate=prop:currencyValidator",
"clientId=prop:context.propertyId" })
private TextField currency;
public PropertyEditContext getContext() { return _context; }
public FieldValidator getCurrencyValidator()
{
return context.getValidator(_currency);
}
public Translator getCurrencyTranslator()
{
return ...;
}
}
+---+
The editor is a block inside the component template:
+---+
<t:block id="currency">
<t:label for="currency"/>
<t:textfield t:id="currency" size="10"/>
</t:block>
+--+
Finally, we tell the BeanEditForm component about the editor via a contribution to the
{{{../../apidocs/org/apache/tapestry5/services/BeanBlockSource.html}BeanBlockSource}} service:
+---+
public static void contributeBeanBlockSource(Configuration<BeanBlockContribution> configuration)
{
configuration.add(new BeanBlockContribution("currency", "AppPropertyEditBlocks", "currency", true));
}
+--+
Now, when the BeanEditForm sees a property of type BigDecimal, it will map that to datatype "currency" and from there
to the currency block of the AppPropertyEditBlocks page of the application.