Template directives

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.

Localization

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 ResourceBundles, 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.

[Note]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 &lt;. 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.

$remove$ jwcid

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.

[Warning]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.

$content$ jwcid

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.