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