A Tapestry page consists of a number of components. These components communicate with, and coordinate with, the page (and each other) via parameters.
A component parameter has a unique name and a type (a Java class, interface, or primitive type name).
The <parameter>
component specification element is used to define formal component parameters.
In a traditional desktop application, components have properties. A controller may set the properties of a component, but that's it: properties are write-and-forget.
The Tapestry model is a little more complex. A component's parameters are bound to properties of the enclosing page. The component is allowed to read its parameter, to access the page property the parameter is bound to. A component may also update its parameter, to force a change to the bound page property.
The vast majority of components simply read their parameters. Updating parameters is more rare; the most
common components that update their parameters are form control components such as TextField
or Checkbox
.
Because bindings are in the form of OGNL expressions, the property bound to a component parameter may not directly be a property of the page ... using a property sequence allows great flexibility.
Not all parameter bindings are writable. So far, the examples have been for parameters bound using
the <binding>
specification element (or the equivalent use of the ognl:
prefix
in an HTML template). Invariant bindings are also possible--these are bindings directly to fixed
values that never change and can't be updated. The <static-binding>
element is invariant; it's
HTML template equivalent is a attribute with no prefix. Likewise, the
<message-binding>
element, and the message:
prefix on an attribute, are invariant.
To understand how Tapestry parameters work, you must understand how the binding objects work (even
though, as we'll see, the binding objects are typically hidden). When a component needs access to
a bound parameter value, it will invoke the method getObject()
on IBinding
Updating a parameter is the same way, except that the method is
setObject()
. Most of the implementations of IBinding
(those for literal strings
and localize messages), will throw an exception immediately, since they are invariant.
These flows are complicated by the fact that parameters may be optional; so not only do you need to acquire the
correct binding object (method getBinding()
defined in IComponent
),
but your code must be prepared for that object to be null (if the parameter
is optional).
Accessing and manipulating the IBinding
objects is tedious, so Tapestry has an alternate approach. Parameters may
be represented as connected parameter properties that hide the existence of
the binding objects entirely. If you component needs to know the value bound to a parameter, it can
read the connected parameter property. If it wants to update the property bound to the parameter, the component
will update the connected parameter. This is a much more natural approach, but requires a little bit of setup.
As with specified properties, Tapestry will fabricate an enhanced subclass with the necessary instance variables, accessor methods, and cleanup code.
Connected parameters are controlled by the direction
attribute of the <parameter>
element.
[4]
There are four values:
in
, form
, auto
and custom
. The default is custom
, which
does not create a connected parameter property at all.
The majority of component parameters are read-only, and are only actually used
within the component's renderComponent()
method ... the method that
actually produces HTML output. For such components, direction in
is the standard, efficient choice.
The connected parameter for each component is set just before renderComponent()
is invoked. The parameter is reset to its initial value just after
renderComponent()
is invoked.
Each component has a ParameterManager
, whose responsibility is to set and reset connected
parameter properties.
For invariant bindings (literal strings and such), the ParameterManager will only set the connected parameter property once,
and does not reset the property after renderComponent()
.
Warning | |
---|---|
If your component has any listener methods that need to access a parameter value, then you
can't use direction |
Components, such as TextField
or Checkbox
, that produce form control elements are the
most likely candidates for updating their parameters. The read a parameter (usually named
value
) when they render. When the form is submitted, the same components
read a query parameter and update their value
parameter.
The form
direction simplifies this. For the most part, form
is the same as in
. The diffference is, when the form is submitted, after the
component's renderComponent()
method has been invoked, the connected parameter property
is read and used to update the binding (that is, invoke the binding object's setObject()
method).
The previous direction values, in
and form
, have limitations. The value may only be
accessed from within the component's renderComponent()
method. That's often insufficient,
especially when the component has a listener method that needs access to a parameter.
Direction auto
doesn't use the ParameterManager
. Instead, the connected parameter property
is synthetic. Reading the property immediately turns around and invokes
IBinding
's getObject()
method. Updating the property invokes
the IBinding
's setObject()
function.
This can be a bit less efficient than direction in
, as the OGNL expression may be
evaluated multiple times. In Tapestry 3.0, the parameter must also be required. Future releases
of Tapestry will relax these limitations.
Removing parameter directions | |
---|---|
Parameter directions are a bit of a sore spot: you must make too many decisions about how to use them, especially
in terms of render-time-only vs. listener method. Direction |
The custom
direction, which is the default, does not
create a connected parameter property. Your code is still responsible for
accessing the IBinding
object (via the getBinding()
method of
IComponent
) and for invoking methods on the binding object.
[4] The name, "direction", made sense initially, but is now a bit confusing. It probably should have been called "processing" or "connection-type".