DynamicBlock - Choosing Tapestry Components at Runtime
Working with the Tapestry web component model is great. The
speed with which I can put together the interface is awesome. However, a
component cannot be used unless it is defined somewhere, as an explicit
component, or implicit in the page template. In situations where I want to use
different components in the page depending on user preferences, or the type of
object presented by the page I can use the RenderBlock /Block component
combination.RenderBlock provides a
mechanism to make parts of the page configurable by choosing a Block component
at runtime. However the Block components to be used must all be defined
somewhere in the page, or another
component:
<span jwcid="@RenderBlock" block="ognl:block" />
<span jwcid="bigBlock@Block">
<span jwcid="@BigComponent" />
<span>
<span jwcid="smallBlock@Block">
<span jwcid="@SmallComponent" />
<span>
And determine the block at
runtime:
public Block getBlock() {
if (getUser().prefersSmallComponent()) {
return getComponent("smallBlock");
}
return getComponent("bigBlock");
}
However there are situations where I might
want to provide a high level of flexibility in adding new components to an
application, perhaps for skinning, or co-branding, or a seasonal look and feel
(Christmas, Easter) or an application where the types of information displayed
is added to over time. In teams dominated by creative or design staff without a
high degree of technical background an environment where components can be
pushed and previewed without a full system rebuild/redeploy cycle provides a way
to try things out with real data in a running application.
The ideal solution would be to define
a component with an OGNL
expression:
<span jwcid="ognl:'someId@'+determineComponentName" />
I've tried that and it does not work and my
guess is that it would be hard to implement, so I looked again at Block and
RenderBlock. What if I could generate the Block components at runtime when I
needed them? Instead of declaring them in the component or a page. I would then
have what I needed, logic that detemined which component to use, by name only
and a component that did all the housekeeping of constructing the Block
components for RenderBlock. An hour or so digging around the template and
specification loading code and I had the bones of a DynamicBlock component.
DynamicBlock has a single, required parameter:
componentName,
a string which can be returned from an OGNL expression. I can use it like
this:
<component id="switchBlock" type="DynamicBlock">
<binding name="componentName" expression="selectedComponentName" />
</component>
And never define a Block. Now that the
choice of component is the result of logic and templates do not have to be
edited to add new choices I have the flexibility to add new components at will.
If I am using a custom template and specification resolver delegate I can do
this without restarting the application. If my component choosing logic is
scripted with Groovy , via Groovestry I can modify the component choosing
logic, again without a restart.This
DynamicBlock is bare-bones, I need to figure out how to pass any body wrapped by
the DynamicBlock into the component to be rendered and how to pass parameters
from the DynamicBlock to the
component.Any resemblance to WOSwitchComponent is purely
intentional.See the example at: http://examples.mjhenderson.com/dynamicblock/app
Get the source code: dynamicblock-src.tgz
Posted: Thu - September 2, 2004 at 06:12 AM
|