In this chapter, we'll create a new web application that will show some dynamic content. We'll also begin to show some interactivity by adding a link to the page. Our dynamic content will simply be to show the current date and time. The interactivity will be a link to refresh the page. It all looks like this:
Clicking the word "here" will update the page showing the new data and time. Not incredibly interactive, but it's a start.
The code for this section of the tutorial is in the package tutorial.simple.
The application specification is almost identical to the Hello World example:
Figure 4.2. Simple.application
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE application PUBLIC "-//Howard Lewis Ship//Tapestry Specification 1.3//EN" "http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"> <application name="Simple Tutorial" engine-class="org.apache.tapestry.engine.SimpleEngine"> <page name="Home" specification-path="/tutorial/simple/Home.page"/> </application> |
Things only begin to get more interesting when we look at the HTML template for the home page:
Figure 4.3. Home.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Simple</title> </head> <body> This application demonstrates some dynamic behavior using Tapestry components. <p>The current date and time is: <b><span jwcid="insertDate">Current Date</span></b> <p>Click <a jwcid="refresh">here</a> to refresh. </body> </html> |
This looks like ordinary HTML, except for the special jwcid attribute. "jwc" is short for "Java Web Component"; these attributes identify the tag as a placeholder for a dynamic Tapestry component.
We have two components. The first inserts the current date and time into the HTML response. The second component creates a hyperlink that refreshes the page when clicked.
One of the goals of Tapestry is that the HTML should have the minimum amount of special markup. This is demonstrated here ... the dynamic component tags blend into the standard HTML of the template. We also don't confuse the HTML by explaining exactly what an insertDate or refresh is; that comes out of the specification (described shortly). The ids used here are meaningful only to the developer [3], the particular type and configuration of each component is defined in the component specification.
Tapestry doesn't really care what HTML tag you use, as long as you balance the tag correctly. In fact, it ignores the tag entirely: the refresh component above could just has easily been identified with a <span> tag, or any other tag for that matter. Tapestry is only interested in the structure of the HTML template. The fact that you can use meaningful tags is a convienience; it allows a Tapestry HTML template to be previewed in a WYSIWYG HTML editor, such as HomeSite. Additionally, Tapestry edits out the content of tags for components that don't wrap around other content: the insertDate component in this example. This allows a preview values to be kept in the template.
Very significant is the fact that a Tapestry component can wrap around other elements of the template. The refresh component wraps around the word "here". What this means is that the refresh component will get a chance to emit some HTML (an <a> hyperlink tag), then emit the HTML it wraps (the word "here"), then get a chance to emit more HTML (the </a> closing tag).
What's more important is that the component can not only wrap static HTML from the template (as shown in this example), but may wrap around other Tapestry components and those components may themselves wrap text and components, to whatever depth is required.
And, as we'll see in later chapters, a Tapestry component itself may have a template and more components inside of it. In a real application, the single page of HTML produced by the framework may be the product of dozens of components, effectively "woven" from dozens of HTML templates.
Again, the HTML template doesn't define what the components are, it is simply a mix of static HTML that will be passed directly back to the client web browser, with a few placeholders (the tags with the jwcid attribute) for where dynamic content will be plugged in.
The page's component specification defines what types of components are used and how data moves between the application, page and any components.
Figure 4.4. Home.page
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE page-specification PUBLIC "-//Howard Lewis Ship//Tapestry Specification 1.3//EN" "http://tapestry.sf.net/dtd/Tapestry_1_3.dtd"> <page-specification class="tutorial.simple.Home"> <component id="insertDate" type="Insert"> <binding name="value" expression="currentDate"/> </component> <component id="refresh" type="PageLink"> <static-binding name="page">Home</static-binding> </component> </page-specification> |
Here's what all that means: The Home page is implemented with a custom class, tutorial.simple.Home. It contains two components, insertDate and refresh.
The two components used within this page are provided by the Tapestry framework.
The insertDate component is type Insert. Insert components have a value parameter used to specify what should be inserted into the HTML produced by the page. The insertDate component has its value parameter bound to a JavaBeans property of its container (the page), the currentDate property.
The refresh component is type PageLink, meaning it creates a link to some other page in the application. PageLink components have a parameter, named page, which defines the name of the page to navigate to. The name is matched against a page named in the application specification.
In this case, we only have one page in our application (named "Home"), so we can use a static binding for the page parameter. A static binding provides a value for the component parameter statically, the same value every time. The value is defined right in the specification.
That just leaves the implementation of the Home page component:
Figure 4.5. Home.java
package tutorial.simple; import java.util.Date; import org.apache.tapestry.html.BasePage; public class Home extends BasePage { public Date getCurrentDate() { return new Date(); } } |
Home implements a read-only JavaBeans property, currentDate. This is the same currentDate that the insertDate component needs. When asked for the current date, the Home object returns a new instance of the java.util.Date object.
The insertDate component converts objects into strings by invoking toString() on the object.
Now all the bits and pieces are working together.
Run the application, and use the View Source command to examine the HTML generated by by framework.
Figure 4.6. HTML generated for Home page
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Simple</title> </head> <body> This application demonstrates some dynamic behavior using Tapestry components. <p>The current date and time is: <b>Fri Nov 23 17:05:53 PST 2001</b> <p>Click <a href="/tutorial/simple...">here</a> to refresh. </body> </html> |
This should look very familiar, in that it is mostly the same as the HTML template for the page. Tapestry not only inserted simple text (the current date and time, obtained from an java.util.Date object), but the refresh component inserted the <a> and </a> tags, and created an appropriate URL for the href attribute.