------ Ajax/DHTML Guide - Basics ------ Jesse Kuhnert ------ 24 June 2007 ------ Ajax Development Basics The overall concept behind the majority of built in AJAX functionality in Tapestry is that everything should work exactly the same way in your pages/components in AJAX requests as it does during normal page rendering. There are a few corner cases that have no real intuitive XHR equivalent which we will discuss as well. * Including JavaScript in your HTML Template One of the first things you will need to do is make sure you have the necessary javascript includes. Tapestry makes extensive use of the {{{http://dojotoolkit.org}Dojo}} javascript toolkit and provides a couple of options for including it for you automatically via the {{{../components/general/shell.html}Shell}} or {{{../components/general/scriptincludes.html}ScriptIncludes}} components. An example of defining a basic outer layer using the {{{../components/general/shell.html}Shell}} component would look like: +-------------------------------------------------------------------------------------------------------------

Basic javascript inclusion sample.

+------------------------------------------------------------------------------------------------------------- * Updating Components: The universal updateComponents parameter One of the most frequently used pieces of ajax functionality is the <<>> parameter that is now implemented by all of the core Tapestry components - where it makes sense. Some of the more familiar of these are {{{../components/link/directlink.html}DirectLink}}, {{{../components/form/linksubmit.html}LinkSubmit}}, {{{../components/form/form.html}Form}}, {{{../components/form/imagesubmit.html}ImageSubmit}} and {{{../components/form/submit.html}Submit}}. We'll build on our previous example and use the {{{../components/link/directlink.html}DirectLink}} component to refresh a current time display. +------------------------------------------------------------------------------------------------------------- ..

Basic javascript inclusion sample.

Refresh time.

.. +------------------------------------------------------------------------------------------------------------- The corresponding java page class: +------------------------------------------------------------------------------------------------------------- .. public abstract BasicAjax extends BasePage { public abstract void setTime(Date time); public void setTime() { setTime(new java.util.Date()); } } +------------------------------------------------------------------------------------------------------------- That's it! Building and running this small example should be all you need to start using the very basic AJAX functionality provided by Tapestry. ** updateComponents == IComponent.getClientId() There are a few subtle things happening here that may not be apparent at first glance. One of the more important changes in Tapestry 4.1.x was the addition of the {{{../apidocs/org/apache/tapestry/IComponent.html#getClientId()}IComponent.getClientId()}} method to all component classes. When dealing with client side javascript functionality the unique client side element id of the html elements your components generate becomes very important. What this method gives you is the unique element id attribute that will be written for any given component depending on the context in which you call it. If you are in a {{{../components/general/for.html}For}} loop then the <<>> will automatically be incremented for each loop and always represent the unique value of the component. The same semantics work whether in {{{../components/form/form.html}Forms}} / {{{../components/general/for.html}For}} or any other nested type of structure. This has been one of key changes in the API that has made the AJAX functionality provided easier to work with. In our example the <<>> parameter given made use of the new smart auto binding functionality added to the framework recently, but you could write the equivalent parameter using an ognl expression as well: +------------------------------------------------------------------------------------------------------------- Refresh time +------------------------------------------------------------------------------------------------------------- Things can get a lot uglier once you start trying to update multiple components: +------------------------------------------------------------------------------------------------------------- Refresh time +------------------------------------------------------------------------------------------------------------- The much easier simple string list <<>> is not only shorter / easier to understand but also more efficient and robust in many different circumstances. For instance, the auto binding functionality would be able to detect that you were trying to update a component contained within one of your own custom components as well as a component on the page containing your component and wire up and find all the correct <<>> values for you automatically. The thing to walk away with here is that in 98% of the cases you run in to this style of <<>> syntax is the way to go: +------------------------------------------------------------------------------------------------------------- Refresh time +------------------------------------------------------------------------------------------------------------- ** How clientId values are generated The actual value that is output by a component is determined by a number of rules depending on the context and type of component involved. Of course, no id will be generated at all if your custom components don't call the new {{{../apidocs/org/apache/tapestry/AbstractComponent.html#renderIdAttribute(org.apache.tapestry.IMarkupWriter,%20org.apache.tapestry.IRequestCycle)}AbstractComponent.renderIdAttribute(IMarkupWriter, IRequestCycle)}} method provided in the base <<>> superclass that most components derive from. When that method is called the rules for determining what value to render out in to the generated html element are evaluated in this order: [[1]] <> - If the definition of the component being rendered had an informal <<>> parameter specified that will take higher precendence than anything else. It may eventually end up being output as <<>> if your component is being rendered in a loop but it will still use the id as the basis for its client side id values. +----------------------------------------------
Content
+---------------------------------------------- [[1]] <> - If no informal id parameter is bound then the next candidate used to seed the clientIds is the value returned from <<>>. This will be the id specified in your <<>> file or the id assigned in your html snippet as in: +----------------------------------------------
Content
or .page file: +---------------------------------------------- [[1]] <
> - If the component in question implements {{{../apidocs/org/apache/tapestry/form/IFormComponent.html}IFormComponent}} then the same semantics as above apply with the caveat that the inner workings of client id generation work slightly differently here as they are synchronized back with the normal {{{../components/form/form.html}Form}} input name/clientId semantics that Tapestry has always had. [] The previously mentioned <<>> method is the crucual piece that anyone writing custom components will need to make sure to call if you plan on overriding <<>>. One very simplistic example of calling this new base method would be: +------------------------------------------------------------------------------------------------------------- .. public abstract MyCustomComponent extends AbstractComponent { public void renderComponent(IMarkupWriter writer, IRequestCycle cycle) { writer.begin("div"); writer.renderIdAttribute(writer, cycle); <----------------------------------* writer.renderInformalParamters(writer, cycle); writer.print("Custom text content.."); writer.end(); } } +------------------------------------------------------------------------------------------------------------- ** Common Mistake: Can't update a component not already rendered on the page One of more common questions on the users mailing list inevitably involves this innocent looking block of code modified from our previous examples: +------------------------------------------------------------------------------------------------------------- ..

Basic javascript inclusion sample.

Refresh time.

.. +------------------------------------------------------------------------------------------------------------- The problem with this code is that the component being requested to update - <<