Component Parameters

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.

Figure 3.2. Parameter Bindings

Parameter Bindings

Using OGNL, the TextField component's value parameter is bound to the LineItem's quantity property, using the OGNL expression lineItem.quantity, and the Insert component's value parameter is bound to the Product's name property using the OGNL expression lineItem.product.name.

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.

Using Bindings

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

Figure 3.3. Reading a Parameter

Reading a Parameter

The getObject() method on IBinding will (if the binding is dynamic) evaluate the OGNL expression (provided in the <binding> specification element) to access a property of the page. The result is that cast or otherwise coerced to a type useful to the component.

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.

Figure 3.4. Writing a Parameter

Writing a Parameter

The setObject() method will use OGNL to update a page property.

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).

Connected Parameter Properties

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.

Direction: in

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.

Figure 3.5. ParameterManager and renderComponent()

ParameterManager and renderComponent()

The ParameterManager will read the values bound to each parameter, and update the connected parameter property before the component's renderComponent() method is invoked. The ParameterManager cleans up after renderComponent() is invoked.

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]Warning

If your component has any listener methods that need to access a parameter value, then you can't use direction in (or direction form). Listener methods are invoked outside of the page rendering process, when value stored in the connected parameter property is not set. You must use direction auto or custom in such cases.

Direction: form

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).

Direction: auto

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.

[Note]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 auto is too limited and possibly too inefficient. Tapestry 3.1 should address these limitations by improving direction auto. Instead of specifying a direction, you'll specify how long the component can cache the value obtained from the binding object (no caching, or only while the component is rendering, or until the page finishes rendering).

Direction: custom

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".