Page and component templates

Unlike many other web frameworks, such as Struts or WebWork , Tapestry does not "plug into" an external templating system such as JavaServer Pages or Velocity . Instead, Tapestry integrates its own templating system.

Tapestry templates are designed to look like valid HTML files (component HTML templates will just be snippets of HTML rather than complete pages). Tapestry "hides" its extensions into special attributes of ordinary HTML elements.

Don't be fooled by the terminology; we say "HTML templates" because that is the prevalent use of Tapestry ... but Tapestry is equally adept at WML or XML.

Template locations

The general rule of thumb is that a page's HTML template is simply an HTML file, stored in the context root directory. That is, you'll have a MyPage.html HTML template, a WEB-INF/ MyPage.page page specification, and a MyPage class, in some Java package.

Tapestry always starts knowing the name of the page and the location of the page's specification when it searches for the page's HTML template. Starting with this, it performs the following search:

  • In the same location as the specification
  • In the web application's context root directory (if the page is an application page, not a page from a component library)
In addition, any HTML template in the web application context is considered a page, even if there is no matching page specification. For simple pages that don't need to have any page-specific logic or properties, there's no need for a page specification. Such a page may still use the special Tapestry attributes (described in the following sections). Finally, with some minor configuration it is possible to change the extension used for templates. For example, if you are developing a WML application, you may wish to name your application's template files with the extension .wml.

Template Contents

Tapestry templates contain a mix of the following elements:

  • Static HTML markup
  • Tapestry components
  • Localized messages
  • Special template directives
Usually, about 90% of a template is ordinary HTML markup. Hidden inside that markup are particular tags that are placeholders for Tapestry components; these tags are recognized by the presence of the jwcid attribute. "JWC" is short for "Java Web Component", and was chosen as the "magic" attribute so as not to conflict with any real HTML attribute. Tapestry's parser is quite flexible, accepting all kinds of invalid HTML markup. That is, attributes don't have to be quoted. Start and end tags don't have to balance. Case is ignored when matching start and end tags. Basically, the kind of ugly HTML you'll find "in the field" is accepted. The goal is to allow you to preview your HTML templates using a WYSIWYG HTML editor (or even an ordinary web browser). The editor will ignore the undefined HTML attributes (such as jwcid). A larger goal is to support real project teams: The special markup for Tapestry is unobtrusive, even invisible. This allows an HTML designer to work on a template without breaking the dynamic portions of it. Importantly, the designers can work with their normal tools and editors. This is completely unlike JSPs, where the changes to support dynamic output are extremely intrusive and result in a file that is meaningless to an HTML editor.

Components in templates

Components can be placed anywhere inside a template, simply by adding the jwcid attribute to any existing tag. For example:

<html>
  <head>
    <title>Example HTML Template</title>
  </head>
  <body
    <span jwcid="border">
    
      Hello,
      <span jwcid="@Insert" value="ognl:user.name">Joe User</span>
    </span>
  </body>
</html>

The first span is a reference to a declared component ; the type and parameters of the component are declared in the page's specification.

The second span is an implicit component ; the type of the component is Insert . The value parameter is bound to the OGNL expression user.name .

The point of all this is that the HTML template should preview properly in a WYSIWYG HTML editor. Unlike Velocity or JSPs, there are no strange directives to get in the way of a preview (or necessitate a special editting tool), Tapestry hides what's needed inside existing tags; at worst, it adds a few non-standard attributes (such as jwcid) to tags. This rarely causes a problem with most HTML editors.

Templates may contain components using two different styles. Declared components are little more than a placeholder; the type of the component is defined elsewhere, in the page (or component) specification.

Alternately, an implicit component can be defined in place, by preceding the component type with an "@" symbol. Tapestry includes over forty components with the framework, additional components may be created as part of your application, or may be provided inside a component library.

In the above example, a <span> was used for both components. Tapestry doesn't care what tag is used for a component, as long as the start and end tags for components balance (it doesn't even care if the case of the start tag matches the case of the end tag). The example could just as easily use <div> or <fred>, the rendered page sent back to the client web browser will be the same.

The default attribute name is jwcid , but there are occasions when that is not desirable. The org.apache.tapestry.jwcid-attribute-name configuration property allows you to control the template parser's behavior.

Component bodies

In Tapestry, each component is responsible for rendering itself and its body . A component's body is the portion of its page's template that its tags encloses. The Tapestry HTML template parser is responsible for dividing up the template into chunks: blocks of static HTML, component start tags (recognized by the jwcid attribute) and matching component end tags. It is quite forgiving about case, quotes (which may be single quotes, double quotes, or even omitted), and missing close tags (except for components, which must be balanced).

Note:

More correct would be to say "its container's template" as a component may be contained within another component. For simplicities sake, we'll describe this as if it was always a simple two-level heirarchy even though practical Tapestry applications can be many levels deep.

Component templates and bodies

In most cases, a component will make use of its body; it simply controls if, when and how often its body is rendered (when rendering the HTML response sent to the client). Other components, such as Insert , have no use for their bodies, which they discard. Each component declares in its own specification (the allow-body attribute of the <component-specification> ) whether is allows or discards its body.

In the previous example, the Insert component had a body, the text "Joe User". This supports WYSIWYG preview; the text will be displayed when previewing. Since the Insert component discards its body, this text will not be used at runtime, instead the OGNL expression user.name will be evaluated and the result inserted into the response.

Warning:

If you put a component inside the body of an Insert (or any other component that discards its body), then Tapestry will throw an exception. You aren't allowed to create a component simply to discard it.

Component ids

Every component in Tapestry has its own id. In the above example, the first component has the id "border". The second component is anonymous; the framework provides a unique id for the component since one was not supplied in the HTML template. The framework provided id is built from the component's type; this component would have an id of $Insert ; other Insert components would have ids $Insert$0 , $Insert$1 , etc.

A component's id must only be unique within its immediate container. Pages are top-level containers, but components may have their own templates, and so can also contain other components.

Implicit components can also have a specific id, by placing the id in front of the "@" symbol:

  <span jwcid="insert@Insert" value="ognl:user.name">Joe User</span>

The component is still implicit; nothing about the component would go in the specification, but the id of the component would be insert .

Providing explicit ids for your components is rarely required, but often beneficial. It is especially useful for form control components.

Each component may only appear once in the template. You simply can't use the same component repatedly ... but you can duplicate a component fairly easily; make the component a declared component, then use the copy-of attribute of the <component> element to create clones of the component with new ids.

Specifying parameters

Components are configured by binding their parameters. In a page or component specification, the <binding> element is used to bind component parameters.

Inside an HTML template, attributes of the tag are used to bind parameters. This can be very succinct. In some cases where an OGNL expression is used, the value can become quite long or complex ... in which case, converting the component to be a declared component (that is, defined in the page or component specification) and using the <binding> element will be more manageable.

Tapestry will merge together parameter bindings in the specification with those provided directly in the template. Generally speaking, conflicts (the same parameter bound in both places) will be an error. The exception is when the parameter bound in the HTML template, as an attribute, is a literal string value ... in which case, Tapestry assumes that the attribute value is there for WYSIWYG purposes and is quietly ignored.

Components may have both formal and informal parameters. The component specification defines each formal parameters using the <parameter> element, and a component indicates whether it accepts or rejects informal parameters with the allow-informal-parameters attribute of the <component-specification> element.

Informal parameters are not limited to simply strings; using binding reference prefixes, it is possible for them to be OGNL expressions, references to assets, or anything else.

If a component does not allow informal parameters, then attempting to bind any parameters (beyond the formal set of parameters for the component) is an error. The exception to this is literal values in the template, which are (again) assumed to be there for WYSIWYG purposes, and quietly ignored.

Two final notes about informal parameters:

  • Informal parameters in the template are assumed to be literal strings unless they have a binding prefix (i.e., "ognl:", "message:", or so forth).
  • Informal parameters are normally converted to string values and added as additional attributes in the output markup. A special case is when the value for an informal parameter is an IAsset (possibly specified with the "asset:" prefix), in which case the attribute value will be the URL for the asset.

Seperation of Concerns

Before Tapestry 3.0, there was a more clear separation of concerns. The template could only have declared components (not implicit), and any informal attributes in the template were always static values. The type of the component and all its formal parameters were always expressed in the specification. The template was very much focused on presentation, and the specification was very much focused on business logic. There were always minor exceptions to the rules, but in general, seperation of concerns was very good. With Tapestry 3.0, you can do more in the HTML template, and the specification file is much less important ... but the seperation of concerns is much more blurred together. It is very much acceptible to mix and match these approaches, even within a single page. In general, when learning Tapestry, or when prototyping, it is completely appopriate to do as much as possible in the HTML template. For large and complex applications, there are benefits to moving as much of the dynamic logic as possible out of the template and into the specification.

Formal and informal parameters

Components may accept two types of parameters: formal and informal . Formal parameters are those defined in the component's specification, using the <parameter> element. Informal parameters are additional parameters, beyond those known when the component was created.

The majority of components that accept informal parameters simply emit the informal parameters as additional attributes. Why is that useful? Because it allows you to specify common HTML attributes such as class or id, or JavaScript event handlers, without requiring that each component define each possible HTML attribute (the list of which expands all the time).

Note:

If you are used to developing with JSPs and JSP tags, this will be quite a difference. JSP tags have the equivalent of formal parameters (they are called "tag attributes"), but nothing like informal parameters. Often a relatively simply JSP tag must be bloated with dozens of extra attributes, to support arbitrary HTML attributes.

Informal and formal parameters can be specified in either the specification or in the template. Informal parameters are not limited to literal strings, you may use the ognl: and message: prefixes with them as well.

Not all components allow informal parameters; this is controlled by the allow-informal-parameters attribute of the <component-specification> element. Many components do not map directly to an HTML element, those are the ones that do not allow informal parameters. If a component forbids informal parameters, then any informal parameters in the specification or the template will result in errors, with one exception: static strings in the HTML template are simply ignored when informal parameters are forbidden; they are presumed to be there only to support WYSIWYG preview.

Another conflict can occur when the HTML template specified an attribute that the component needs to render itself. For example, the DirectLink component generates a <a> tag, and needs to control the href attribute. However, for preview purposes, it often will be written into the HTML template as:

  <a jwcid="@DirectLink" listener="listener:. . ." href="#"> . . . </a>

This creates a conflict: will the template href (the literal string "#") be used, or the dynamically generated URL produced by the DirectLink component, or both? The answer is: the component wins. The href attribute in the template is ignored.

Each component declares a list of reserved names using the <reserved-parameter> element; these are names which are not allowed as informal parameters, because the component generates the named attribute itself, and doesn't want the value it writes to be overriden by an informal parameter. Case is ignored when comparing attribute names to reserved names.

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 localization features. As we've seen, it is possible to access the messages for a page or component using the message: prefix on a component parameter.

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, class, or style, 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 the attribute value 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:

<table>
  <tr>
    <th>First Name</th>
    <th>Last Name</h>
  </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 For , 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.

Note:

Normally, $remove$ would not be a valid component id, because it contains a dollar sign.

With this in mind, the template can be rewritten:

<table>
  <tr>
    <th>First Name</th>
    <th>Last Name</h>
  </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 table row for each row read from the database, and "Frank Smith" and "Jane Jones" nowhere to be seen.

Warning:

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 of interesting 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 ref="stylesheet" href="style.css" type="text/css"/>
  </head>
  <body>
    <span jwcid="border">
    
      <!-- Page specific content: -->
      
      <form jwcid=". . .">
        . . .
      </form>
    </span>
  </body>
</html>

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 with 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) as the only part that should be used. The $content$ component id is used for this purpose:

<html>
  <head>
    <title>Home page</title>
    <link ref="stylesheet" href="style.css" type="text/css"/>
  </head>
  <body jwcid="$content$">
    <span jwcid="border">
    
      <!-- Page specific content: -->
      
      <form jwcid=". . .">
        . . .
      </form>
    </span>
  </body>
</html>

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.