tapestry action event caveat when using persistent state

tapestry5 is a wonderful web development framework. It is very powerful and gives great help when building web applications. However, there is a little snag which I have been bitten by twice already…

There is a lot of magic going on in the background. You can wrote some code which reacts to an “action” event, typically by clicking on a link.

The template would look like

<t:actionlink t:id="add">delete</t:actionlink>

And the code would be

Object onActionFromAdd()
{
    // do your magic, possibly returning a link
}

All very simple, but in many cases you need to have some more information when the link is fired. In most cases, this can be done by keeping some persistent state. While this has the disadvantage that a session will be required, it has the advantage that the state does not need to be sent to/received from the client side, requiring coercers and/or converters to be written. In some cases the serialization is simply problematic.

This works nicely, but fails totally when the action is embedded in a loop component. The persistent state is stored in the session based on the component nesting of your page. There is no distinction made between the iterations in a loop. When using persitent state, you will allways be working on the last instace in the loop.

This means that the state can only be correctly referred to when the context parameter is used.

<t:actionlink t:id="add" context="id">delete</t:actionlink>

Object onActionFromAdd( String id )
{
    // do your magic, possibly returning a link
}

The context can have any type (you may need to provide a coercer and converter).

The alternative is that you just use an id as context, and store the state in some kind of cache service. In that case the cache could be persisted. Unfortunately though, this requires some work. One way to do this would be using the following skeleton on the component which contains the actionlink components

@Inject
private MyCache cache;

Object onActionFromAdd( String contextId )
{
    MyCachedObject = cache.get( contextId );

    // do your magic, possibly returning a link
}

void beginRender()
{
    // ... for each of the iterations in the loop
    cache.put( new MyCachedObject( ... ) );
}

And the following code should be included in your application module (usually called AppModule) class.

public static MyCache buildMyCache()
{
    return new MyCacheImpl();
}

Leave a Reply

Your email address will not be published. Required fields are marked *

question razz sad evil exclaim smile redface biggrin surprised eek confused cool lol mad twisted rolleyes wink idea arrow neutral cry mrgreen

*