blob: 533bd36f86a7f01ac19b0c2f7a76fc7126f6f216 [file] [log] [blame]
<chapter id="built-ins">
<title>Using the Built-In Components</title>
<para>
This section will provide examples on a couple of basis non-visual built in components,
that are very, very useful. The &Foreach; component (as you might have guessed from its name)
is used to iterator over a number of element. The other components are used to respond to
various user events, whether via a link or through a form button.
</para>
<section id="builtin-foreach">
<title>Foreach</title>
<para>The &Foreach; component can be used to iterate over some collection of items.
The supplied collection can be either a <varname>java.util.Collection</varname>, <varname>java.lang.Iterator</varname>,
an array of Objects, or a even a single Object (which is treated as a singleton collection). The source parameter
may even be null, in which case the Foreach's body is never renderred.</para>
<para>For each element in the collection, &Foreach; will render whatever is enclosed in the element body.
For example, let us assume the following Java code exists on the page:</para>
<figure>
<title>Foreach Example Code</title>
<programlisting>
public Collection getPeople() {
... code here ...
}
</programlisting>
</figure>
<para>This code would return zero or more instances of Person, defined to be:</para>
<figure>
<title>Interface Declaration, for Person</title>
<programlisting>
interface Person {
String getFirstName();
String getLastName();
int getHeight();
}
</programlisting>
</figure>
<para>We would use the following HTML Template, to show all people in a table:</para>
<figure>
<title>Example Template, for Person objects</title>
<programlisting>
&lt;table&gt;
&lt;span jwcid="people"&gt;
&lt;tr&gt;&lt;td&gt;&lt;span jwcid="name"/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span jwcid="lastname"/&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;span jwcid="height"/&gt; cm&lt;/td&gt;&lt;/tr&gt;
&lt;/span&gt;
&lt;/table&gt;
</programlisting>
</figure>
<para>Notice that the <varname>people</varname> element wraps the other elements.
With the &Foreach; component, it is valid (and required if you want to do anything useful :-)
to have content inside the element. The page specification will include the following:</para>
<figure>
<title>Component Definition for people iterator</title>
<programlisting>
&lt;component id="people" type="Foreach"&gt;
&lt;binding name="source" expression="getPeople()"/&gt;
&lt;/component&gt;
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="components.people.value.name"/&gt;
&lt;/component&gt;
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="components.people.value.lastname"/&gt;
&lt;/component&gt;
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="components.people.value.height"/&gt;
&lt;/component&gt;
</programlisting>
</figure>
<para>This definition binds our iterator in the HTML template,
such that it uses the vale of <varname>getPeople()</varname> in the page class as the source of the collection.
In order to show the values, we use standard &InsertText; components - since for this example we just want to display the information.
</para>
<para>Note the expression being used here, which is rougly equivalent to the following Java code:</para>
<figure>
<title>Foreach Java Code (or thereabouts)</title>
<programlisting>
Map components = getComponents();
Foreach fe = (Foreach)components.get("people");
Person person = (Person)fe.getValue();
</programlisting>
</figure>
<para>What is happening, is that for every object in the source collection,
the &Foreach; component is making it available via it's <varname>getValue()</varname> method.
We can use this to our advantage in the component specification, and reference it directly - as shown above.</para>
<para>This strategy will work for simple cases where all you need to do is access
properties of the individual components during the iteration.
If you require more than this, you can request that the Foreach component 'set' the current value on some other
object, for every object in the source collection. To do this, specify an expression for the value property:</para>
<figure>
<title>Example 'value' property for a Foreach component spec</title>
<programlisting>
&lt;binding name="value" expression="currentPerson"/%gt;
</programlisting>
</figure>
<para>Here, it is expected that there is the following Java code on the page object:</para>
<figure>
<title>Java code, called by Foreach component</title>
<programlisting>
public void setCurrentPerson(Person value) {
// ... Store variable, or some other processing...
}
</programlisting>
</figure>
<para>The Foreach component will call this method for every element that it iterates over in the source collection.
Of course, this approach would typically assume that the <varname>name</varname>, <varname>lastname</varname>
and <varname>height</varname> values were bound a little differently.
For example, if the collection contained only entity identifiers, then the <varname>setCurrentPerson()</varname>
method would use these to lookup the real data from a database or other persistent store.
This information would then be made available via a method such as:</para>
<figure>
<title>Java code, called by Foreach component to get the value</title>
<programlisting>
public Person getPerson() {
// make result of the setCurrentPerson() call available here
}
</programlisting>
</figure>
<para>The component specification expressions would then read:</para>
<figure>
<title>Modified component specifications</title>
<programlisting>
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="person.name"/&gt;
&lt;/component&gt;
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="person.lastname"/&gt;
&lt;/component&gt;
&lt;component id="name" type="InsertText"&gt;
&lt;binding name="value" expression="person.height"/&gt;
&lt;/component&gt;
</programlisting>
</figure>
<para>The expressions here would retrieve the name (or other relevant attribute) of the Person in question
from the <emphasis role="bold">Pages</emphasis> copy of the Person instance.</para>
</section>
<section id="builtin-events">
<title>Handling Events</title>
<para>There are two primary ways by which you can handle user events in Tapestry, the Submit component and Link components.
The primary difference from a user point of view is the way in which they appear.
Submit components appears a HTML submit buttons and Link components appear as simple HTML links.
Submit components were covered in <xref linkend="forms"/> and so will not be covered in further detail in this tutorial.
Please see the &Submit; documentation if you require more information.</para>
<para>Link components come in a variety of forms:</para>
<itemizedlist>
<listitem>GenericLink</listitem>
<listitem>PageLink</listitem>
<listitem>ActionLink</listitem>
<listitem>DirectLink</listitem>
</itemizedlist>
<para>&GenericLink; is used to construct a simple link to some resource.
The resource is not Tapestry specific (i.e: the link can point to anything). GenericLink will render
it's body, and so an HTML template along these lines:</para>
<figure>
<title>Example specification for GenericLink</title>
<programlisting>
&lt;h1&gt;A simple Generic Link&lt;/h1&gt;
Click &lt;span jwcid="google"&gt;here&lt;/span&gt; to perform a search at Google.
</programlisting>
</figure>
<para>would produce the following when rendered:</para>
<figure>
<title>Example GenericLink usage</title>
<mediaobject>
<imageobject>
<imagedata fileref="images/events-genericlink.gif" format="GIF"/>
</imageobject>
</mediaobject>
</figure>
<para>The other types of link mentioned here are Tapestry specific - that is, their parameters name
Tapestry resources, which are then coverted into the correct URL by Tapestry itself.
The &PageLink; component was used in the very first example, <xref linkend="getting-started"/> and
so no example code or output is provided here. If you examine the URLs generated by that
example, note that a correct Tapestry URL has been onstructed for you which also includes any
container specific context information as well.
</para>
<para>The last types of links introduced here are &ActionLink; and &DirectLink;. These components are
very similar in that they both provide the ability for a user's action to be directed to a method on
some object (via a listener). The main difference between the two is that one uses the Direct service,
and the other the Action service. Before going further, a quick explanation is in order:<footnote><para>For
more detailed information, please see the Developer Reference</para></footnote></para>
<para>Both Action and Direct services are used to handle triggering of events, as mentioned previously.
The Direct service will invoke the listener <emphasis>just prior</emphasis> to rendering whereas the Action service will invoke the
listener <emphasis>during</emphasis> rendering. </para>
<para>To clarify (because it took the author a reasonable amount of time to appreciate the difference between the two services),
the action service will call the listener during the rendering phase. As a result, the page will be in the exact state it was, when the
ActionLink was rendered. The Direct service is much simpler, the listener is called before any page rendering takes place. So, why
is this important?</para>
<para><emphasis role="bold">If your listener relies component and object state that changes during the render cycle,
you must use the action service</emphasis></para>
<para>Lets look at how these components are used. Both provide the same properties such as <varname>listener</varname>,
and <varname>disabled</varname>. DirectLink provides an additional property called <varname>parameters</varname>
which is used to set parameter values in the request, for use in the handler. Apart from this, their usage within
the component specification is identical.</para>
<para>To demonstrate, we will create two links, using both the action and direct services. The direct service link will
pass in a parameter to the listener method. In these simple examples, there will not be sufficient complexity
to examine the differences between the two services.<footnote><para>if you find yourself getting StaleLinkExceptions
using DirectLink (or a form component setup in "direct" mode) you should examine your page dependencies,
and if any processing is dependent upon page state then change to using the action service</para></footnote></para>
<para>For this example, we must set the <varname>stateful</varname> to false, since we will not
be using a visit object and creating any kind of session. Here is the HTML, component specification
and java code for this example:</para>
<figure>
<title>Direct/ActionLink example HTML Template</title>
<programlisting>
&lt;h1&gt;A simple Generic Link&lt;/h1&gt;
Click &lt;span jwcid="google"&gt;here&lt;/span&gt; to perform a search at Google.
&lt;h1&gt;ActionLink and DirectLink&lt;/h1&gt;
&lt;p&gt;
This &lt;span jwcid="direct"&gt;direct link&lt;/span&gt; will get the time of day,
save it in a variable of the page, and show it underneath this paragraph
using a InsertText component.
&lt;/p&gt;
&lt;p&gt;
This &lt;span jwcid="action"&gt;action link&lt;/span&gt; will do the same thing
as the direct link (it uses the same listener method). If you would like
to observe the difference in processing, run this example from a console
prompt with debug enabled. The listener method will output the string
&lt;pre&gt;"TIME LISTENER METHOD CALLED"&lt;/pre&gt;. You will notice
the place in the render cycle in which the method is called is different
depending upon the link type that you click.
&lt;/p&gt;
&lt;p&gt;
&lt;center&gt;
&lt;span jwcid="timeofday"&gt;Time of day (or nothing) will go here&lt;/span&gt;
&lt;/center&gt;
&lt;/body&gt;
&lt;/html&gt;
</programlisting>
</figure>
<figure>
<title>Direct/ActionLink component specification</title>
<programlisting>
&lt;page-specification class="tutorial.events.Home"&gt;
&lt;component id="google" type="GenericLink"&gt;
&lt;static-binding name="href"&gt;http://www.google.com&lt;/static-binding&gt;
&lt;/component&gt;
&lt;component id="direct" type="DirectLink"&gt;
&lt;binding name="listener" expression="listeners.timeListener"/&gt;
&lt;binding name="stateful" expression="false"/&gt;
&lt;/component&gt;
&lt;component id="action" type="ActionLink"&gt;
&lt;binding name="listener" expression="listeners.timeListener"/&gt;
&lt;binding name="stateful" expression="false"/&gt;
&lt;/component&gt;
&lt;component id="timeofday" type="InsertText"&gt;
&lt;binding name="value" expression="pageTime"/&gt;
&lt;/component&gt;
&lt;/page-specification&gt;
</programlisting>
</figure>
<figure>
<title>Direct/ActionLink Java code</title>
<programlisting>
/**
* Example code for the event handling section of the tutorial
* @author neil clayton
*/
public class Home extends BasePage {
/**
* Called by both the "direct" and "action" components
*/
public void timeListener(IRequestCycle cycle) throws RequestCycleException {
System.err.println("TIME LISTENER METHOD CALLED");
pageTime = DateFormat.getDateTimeInstance().format(new Date());
}
public String getPageTime() {
return pageTime;
}
/**
* @see net.sf.tapestry.AbstractPage#detach()
*/
public void detach() {
pageTime = null;
}
private String pageTime;
}
</programlisting>
</figure>
<para>The output with debug enabled when using both the direct and action services is shown below.
As you can see, the direct link service is calling the method well before anything happens with the
components on the page. The action service on the other hand is calling the method <emphasis>just
before</emphasis> it renders the &InsertText; component - which is midway through it's render cycle.</para>
<figure>
<title>Example debug ouput for DirectLink call</title>
<programlisting>
...
[java] AbstractDocumentParser [DEBUG] Resolved -//Howard Lewis Ship//Tapestry Specification 1.3//EN as org.xml.sax.InputSource@7a5a19 (for Tapestry_1_3.dtd)
[java] TIME LISTENER METHOD CALLED
[java] DefaultSpecificationSource [DEBUG] Parsing library specification /net/sf/tapestry/Framework.library
...
</programlisting>
</figure>
<figure>
<title>Example debug ouput for ActionLink call</title>
<programlisting>
...
[java] AbstractDocumentParser [DEBUG] Resolved -//Howard Lewis Ship//Tapestry Specification 1.3//EN as org.xml.sax.InputSource@59a34 (for Tapestry_1_3.dtd)
[java] TIME LISTENER METHOD CALLED
[java] DefaultSpecificationSource [DEBUG] Parsing component specification /net/sf/tapestry/html/InsertText.jwc
...
</programlisting>
</figure>
<para>
Finally, it is worth mentioning that these other Tapestry specific link types allow parameters such as
<varname>disabled</varname>,
<varname>scheme</varname>, <varname>port</varname> and <varname>anchor</varname>.
Please see either the Component Reference, Developer Reference or JavaDoc API for further
information on these parameters</para>
</section>
</chapter>