For the most part, a Tapestry page or component template consists of
just static HTML intermixed with tags representing components (containing the jwcid
attribute).
The overarching goal is to make the Tapestry extensions completely invisible.
Tapestry supports a limited number of additional directives that are not about component placement, but instead address other concerns about integrating the efforts of HTML developers with the Java developers responsible for the running application.
Tapestry includes a number of localization features. An important part of which
is to allow each page or component to have its own catalog of localized messages (modeled after the Java
ResourceBundle
class).
The page (or component) message catalog is a collection of .properties
files
that are stored with the page or component specification. They follow the same naming conventions
as for ResourceBundle
s, so component MyComponent
(whose specification file
is MyComponent.jwc
) might have a default
message file of MyComponent.properties
, and a French translation as
MyComponent_fr.properties
.
No global message catalog | |
---|---|
On oft-requested feature for Tapestry is to have a global message catalog, and a way to access that catalog from the individual pages and components. This would allow common messages to be written (and translated) just once. This is a feature that may be added to Tapestry 3.1. |
As we've seen, it is possible to access the messages for a page or component using the
message:
prefix on a component parameter (or use
the <message-binding>
element in a page or component specification).
What about the static text in the template itself? How does that get translated? One possibility would be to make use of the Insert component for each piece of text to be displayed, for example:
<span jwcid="@Insert
" value="message:hello">Hello</span>
This snippet will get the hello
message from the page's message catalog
and insert it into the response. The text inside the <span>
tag is useful for WYSIWYG preview, but will be discarded at runtime in favor of a message string
from the catalog, such as "Hello", "Hola" or "Bonjour" (depending on the selected locale).
Because, in an internationalized application, this scenario will occur with great frequency, Tapestry includes a special directive to perform the equivalent function:
<span key="hello">Hello</span>
This is not an Insert
component, but behaves in a similar way. The tag used must be
<span>
. You do not use the message:
prefix
on the message key (hello
). You can't use OGNL expressions.
Normally, the <span>
does not render, just the message.
However, if you specify any additional attributes in the <span>
tag (such as, commonly,
id
or class
to specify a CSS style),
then the <span>
will render around the message> For example, the template:
<span class="error" key="invalid-access">Invalid Access</span>
might render as:
<span class="error">You do not have the necessary access.</span>
In this example, the placeholder text "Invalid Access" was replaced with a much longer message acquired from the message catalog.
In rare cases, your message may have pre-formatted HTML inside it. Normally, output is
filtered, so that any reserved
HTML characters in a message string are expanded to HTML entities. For example,
a <
will be expanded to <
. If this is not desired,
add raw="true"
to the <span>
. This defeats the filtering, and text in the message
is passed through as-is.
HTML templates in Tapestry serve two purposes. On the one hand, they are used to dynamically render pages that end up in client web browsers. On the other hand, they allow HTML developers to use WYSIWYG editors to modify the pages without running the full application.
We've already seen two ways in which Tapestry accomidates WYSIWYG preview. Informal component parameters may be quietly dropped if they conflict with reserved names defined by the component. Components that discard their body may enclose static text used for WYSIWYG prefix.
In some cases, we need even more direct control over the content of the template. Consider, for example, the following HTML template:
Example 2.2. HTML template with repetative blocks (partial)
<table> <tr> <th>First Name</th> <th>Last Name</th> </tr> <tr jwcid="loop"> <td><span jwcid="insertFirstName">John</span></td> <td><span jwcid="insertLastName">Doe</span></td> </tr> <tr> <td>Frank</td> <td>Smith</td> </tr> <tr> <td>Jane</td> <td>Jones</td> </tr> </table>
This is part of the HTML template that writes out the names of a list of people, perhaps from some kind of database.
When this page renders, the loop
component (presumably a Foreach
, such details
being in the page's specification)
will render its body zero or more times. So we might see rows for "Frank Miller", "Alan Moore" and so forth
(depending on the content of the database).
However, every listing will also include "Frank Smith" and "Jane Jones" ... because the HTML developer left those
two rows in, to ensure that the layout of the table was correct with more than one row.
Tapestry allows a special jwcid
, $remove$
, for this case. A tag so marked is
not a component, but is instead eliminated from the
template. It is used, as in this case, to mark sections of the template that are just there for WYSIWYG preview.
With this in mind, the template can be rewritten:
Example 2.3. Updated HTML template (partial)
<table> <tr> <th>First Name</th> <th>Last Name</th> </tr> <tr jwcid="loop"> <td><span jwcid="insertFirstName">John</span></td> <td><span jwcid="insertLastName">Doe</span></td> </tr> <tr jwcid="$remove$"> <td>Frank</td> <td>Smith</td> </tr> <tr jwcid="$remove$"> <td>Jane</td> <td>Jones</td> </tr> </table>
With the $remove$
blocks in place, the output is as expected, one row for each
row read from the database,
and "Frank Smith" and "Jane Jones" nowhere to be seen.
No components in removed blocks | |
---|---|
It's not allowed to put components inside a removed block. This is effectively the same rule that prevents components from being put inside discarded component bodies. Tapestry will throw an exception if a template violates this rule. |
In Tapestry, components can have their own templates. Because of how components integrate their own templates
with their bodies (the portion from their container's template), you can do a lot ofn iteresting things. It is very
common for a Tapestry application to have a Border component: a component that produces the <html>
,
<head>
, and <body>
tags (along with additional tags
to reference stylesheets), plus some form of navigational control (typically, a nested table and a collection of links and images).
Once again, maintaining the ability to use WYSIWYG preview is a problem. Consider the following:
<html> <head> <title>Home page</title> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body> <span jwcid="border"> <!-- Page specific content: --> <form jwcid=". . ."> . . . </form> </span> </body>
It is quite common for Tapestry applications to have a Border
component, a component that is used by pages to provide the
<html>
, <head>
, and
<body>
tags, plus common navigational features (menus,
copyrights, and so forth). In this example, it is presumed that the border
component is a reference to just such as component.
When this page renders, the page template will provide the <html>
, <head>
and <body>
tags.
Then when the border
component renders, it will again
render those tags (possibly with different attributes, and mixed in to much other stuff).
If we put a $remove$
on the <html>
tag in the page template, the entire page will
be removed, causing runtime exceptions.
Instead, we want to identify that the portion of the template inside
the <body>
tag (on the page template) is the only part that counts). The $content$
component id is used for this purpose:
<html> <head> <title>Home page</title> <link rel="stylesheet" href="style.css" type="text/css"> </head> <body jwcid="$content$"> <span jwcid="border"> <!-- Page specific content: --> <form jwcid=". . ."> . . . </form> </span> </body>
The <body>
tag, the text preceding its open tag, the </body>
tag, and the text following
it are all removed. It's as if the template consisted only of the <span>
tag for the border
component.