In most cases, a developer is not interested in bindings; an easier model for developers is one in which Tapestry uses the parameters and bindings to set properties of the component automatically. Starting in release 2.1, Tapestry includes this behavior, with some constraints and limitations.
Part of the <parameter>
specification for a parameter
is the direction, which can be one of the following values:
Input parameter; the value is drawn from the binding (if bound) and applied to the corresponding component property just before rendering the component.
A parameter which matches the semantics of a form component.
The parameter is treated like an in
parameter when the page is rendering.
When the form containing the component is submitted, the connected property is read (after the component renders), and the value applied to the parameter.
Tapestry does not try to connect the parameter with any property; the component is responsible for accessing the binding and retrieving or setting values.
This type must be used for any kind of output parameter, or for an input parameter where the property may be accessed other than during the rendering of the component.
Why aren't output parameters connectable? | |
---|---|
The problem is the timing of output parameters. Sometimes a parameter is only an output
parameter when the containing form is submitted (for example, any of the form related components).
Sometimes a parameter is output many times (for example, The latter case may always be handled as custom; the former case may be handled in the future. |
Defining a parameter as direction in
causes Tapestry to connect the parameter to the corresponding
property of the component. The parameter specification must identify the Java type of the property.
Properties must be read/write (they must have both
getter and setter methods).
Tapestry will set properties from parameters just before rendering the component. After the component renders, the parameters are cleared; they are returned to inital values. Tapestry reads these initial values just before it sets the properties the first time. This makes it very easy to set defaults for optional parameters: just provide a default value for the correspoinding instance variable.
If the property is connected to an invariant binding (a static or field binding), then the property is set just once, and never cleared.
There are times when the parameter name can't be used as the property name. For example,
the PageLink
component has a page
parameter, the name of the page to link to.
However, all components already have a page
property, the IPage
that ultimately contains them. The specification for the PageLink
component
connects the page
parameter to a property named targetPage
instead.
Defining a connected parameter as required means that the parameter must be bound and the binding must provide a non-null value. A runtime exception is thrown when a required parameter's binding yields a null value.
The following examples show how to declare and use a parameter:
Example 3.1. Connected Parameter - Specification
<specification ...> <parameter name="color" direction="in" java-type="java.awt.Color"/> ...
Example 3.2. Connected Parameter - Java Code
public class ColorComponent extendsAbstractComponent
{ private Color color = Color.RED; public Color getColor() { return color; } public void setColor(Color color) { this.color = color; } protected void renderComponent(IMarkupWriter
writer,IRequestCycle
cycle) throws RequestCycleException { writer.begin("font"); writer.attribute("color", ^RequestContext;.encodeColor(color); renderWrapped(writer, cycle); writer.end(); } }
In this example, the component writes its content inside a <font>
element, with the HTML color attribute set from the color
parameter. RequestContext
includes a static convienience method for converting from a
Color
object to an encoded color
that will be meaningful to a web browser.
The parameter is optional and defaults to red if not specified (that is, if the parameter is not bound).
At runtime, Tapestry will invoke setColor()
first (if the color
parameter is bound). It will then invoke renderComponent()
. Finally (even if
renderComponent()
throws an exception) it will invoke setColor()
again, to restore it back to the default value, Color.RED
.
This code includes a defect: because the parameter is optional, there is nothing to prevent it from being bound to null.