You may create a component that has parameters. Under Tapestry, component parameters are a kind of "named slot" that can be wired up as a source (or sink) of data in a number of ways. This "wiring up" is actually accomplished using binding objects.
Connected Parameters | |
---|---|
Most components use "in" parameters and can have Tapestry connect the parameters to properties of the component automatically. This discussion reveals some inner workings of Tapestry that developers most often no longer need to be aware of. |
The page loader, the object that converts a component specification into an actual
component, is responsible for creating and assigning the bindings. It uses the method
setBinding()
to assign a binding with a name. Your
component can retrieve the binding by name using getBinding()
.
For example, lets create a component that allows the color of
a span of text to be specified using a java.awt.Color
object. The
component has a required parameter named color
. The class's
renderComponent()
method is below:
protected void renderComponent(IMarkupWriter
writer,IRequestCycle
cycle) throws RequestCycleException {IBinding
colorBinding = getBinding("color"); Color color = (Color)colorBinding.getObject("color", Color.class); String encodedColor =RequestContext
.encodeColor(color); writer.begin("font"); writer.attribute("color", encodedColor); renderWrapped(writer, cycle); writer.end(); }
The call to getBinding()
is relatively expensive, since
it involves rummaging around in a Map
and then
casting the result from java.lang.Object
to org.apache.tapestry.IBinding
.
Because bindings are typically set once and then read frequently by the component, implementing them as private instance variables is much more efficient. Tapestry allows for this as an optimization on frequently used components.
The setBinding()
method in
AbstractComponent
checks to see if there is a read/write
JavaBeans property named "name
Binding" of
type IBinding
. In this example, it
would look for the methods getColorBinding()
and setColorBinding()
.
If the methods are found, they are invoked from
getBinding()
and setBinding()
instead of updating the
Map
.
This changes the example to:
privateIBinding
colorBinding; public void setColorBinding(IBinding
value) { colorBinding = value; } publicIBinding
getColorBinding() { return colorBinding; } protected void renderComponent(IMarkupWriter
writer,IRequestCycle
cycle) throws RequestCycleException { Color color = (Color)colorBinding.getObject("color", Color.class); String encodedColor =RequestContext
.encodeColor(color); writer.begin("font"); writer.attribute("color", encodedColor); renderWrapped(writer, cycle); writer.end(); }
This is a trade off; slightly more code for slightly better performance.
There is also a memory bonus; the
Map
used by AbstractComponent
to store the binding will never be created.