As I have been working with Tapestry for more than a few years now it only seemed natural to choose it for this new project I started working on. At it requires a lot of partial rendering and GUI like behaviour I also decided to switch from some custom ajax components to the tacos ones which have an impressing demo.

This short article only shows a simple example which has as purpose to use Tapestry 4 and Tacos components for simple task: a page with parts refreshing periodically to update status information.

I started with a normal tapestry application with just a simple page: TestAjax.

Follow the tacos setup and:

1. Copy all Tacos required files into your application (the tacos jars in WEB-INF/lib, the css and js parts into the web directory). Note: make very attention to the compatibility issues between the tacos and tapestry libraries. I ended up loosing hours because tacos-4.0.0-lib.jar was expecting a function from tapestry-4.0.2.jar and did not worked with a previous tapestry archive.

2. Add the library dependencies in your .application file.

3. Add to your java page some code which returns some dinamic content based on the time the call was made:

    public static final String buttons[] = {"green", "purple", "red" };<br></br>   <br></br>    public String getImgName() {<br></br>        return "button-" + buttons[new Random().nextInt(buttons.length)]<br></br>                + ".gif";<br></br>    } <br></br><br></br>    public String getText1() {<br></br>        SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");<br></br>        return "Last number " + new Random().nextInt(20) + " generated at "<br></br>                + format.format(new Date());<br></br>    }<br></br>

4. Add in your html template something which renders the data:

<img jwcid="@Any" src="ognl:imgName" border="0" align="absmiddle" /><br></br><span jwcid="@Insert" value="ognl:text1" raw="true" /><br></br>

5. Now your page should render the result of the getText1 and a green, purple or red icon depending on the moment you refreshed the page.

6. You can add the javascript required parts to your page template:

<html jwcid="@Shell" title="v4"><br></br>    <script type="text/javascript"><br></br>        djConfig = { isDebug: false, <br></br>        baseRelativePath: "js/dojo/", <br></br>        preventBackButtonFix: false };<br></br>    </script><br></br>    <script type="text/javascript" src="js/dojo/dojo.js"><br></br>    </script><br></br>    <body jwcid="@Body"><br></br>

You can include them later in a PageBody component used everywhere.

7. Now for the dynamic part all you need are 3 elements:

  • an ajax form tacos:AjaxForm
  • an event submit tacos:AjaxEventSubmit which will do the actual submiting of the form upon an event, where you specify what elements to refresh and the effects to apply
  • an tacos:AjaxAutoInvoker which will actualy trigger an event for the EventSubmit
<form jwcid="scriptForm@tacos:AjaxForm"><br></br>            <span jwcid="scriptSubmit@tacos:AjaxEventSubmit"<br></br>                action="listener:formSubmit"<br></br>                updateComponents="ognl:{'updateZone1', 'updateZone2'}"<br></br>                effects="template:{highlight:{any:'[219,128,135], 500, 500'}}" /><br></br>            <span jwcid="autoScriptSubmit@tacos:AjaxAutoInvoker"<br></br>                intervalMilliseconds="2000"<br></br>                target="component:scriptSubmit" /><br></br></form><br></br>

Some observations:

  • even if not initially obvious the updated components are in fact div id’s. In fact you can even put here id of components (divs) which are found elsewhere in the page (in another component scope). Further on when extending this example I wanted to have a PageBody component with some refreshing parts and a rendered body which also contained refreshing parts. Initially I though I have to add 2 forms, 2 events etc. because I had in mind that there will be a visibility problem. In fact I could only refer various id’s from diferent components even if they where not located in the same template. More to that I think the mechanism is really the following: upon refresh the ajax components call the page and then extract the parts with the id’s in the list and then just refresh the page components with matching id’s probably using their innerHtml.
  • this generates some problem when having pages which are rendered based an Id for instance. You have to make sure you store the Id in the form as a hidden to ensure the page call in background will succeed.

8. You will also need to define your update zones by surrounding them with div’s with the id in the updateComponents list.

<div jwcid="@Any" id="updateZone1"><br></br>                <img jwcid="@Any" src="ognl:imgName" border="0"<br></br>                    align="absmiddle" /><br></br>                <span jwcid="@Insert" value="ognl:text1" /><br></br>            </div><br></br><br></br><div jwcid="@Any" id="updateZone2"><br></br>                <img jwcid="@Any" src="ognl:imgName" border="0"<br></br>                    align="absmiddle" /><br></br>                <span jwcid="@Insert" value="ognl:text2" raw="true" /><br></br>            </div><br></br>

An initial mistake I make was to assign the id to the inside component such as the img @Any tag. This will not work of course and if you read the observations above it should now be clear why.

You can find the code attached. All you have to do to get it running is:

  • copy your tapestry and tacos libs in the lib folder
  • copy the js and css (dojo) parts from tacos in the src/context directory
  • create a ${hostname}.build.properties where you configure your tomcat paths
  • run ant
  • start tomcat and go: http://localhost:8080/v4/app?page=TestAjax&service=page
  • have fun