Components in templates

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

Example 2.1. Example HTML template containing components

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

1

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

2 This is a 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.

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 [2] 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).

Figure 2.1. Component templates and bodies

Component templates and bodies

The template is broken into small chunks that are each slotted into a particular component's body.

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]No components in discarded blocks

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 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

Component parameters may always be specified in the page or component specification, using the <binding>, <static-binding> and <message-binding> elements. Prior to Tapestry 3.0, that was the only way ... but with 3.0, it is possible to specify parameters directly within the HTML template.

Using either style of component (declared or implicit), parameters of the component may be bound by adding attributes to the tag. Most attributes bind parameters to a static (unchanging) value, equivalent to using the <static-binding> element in the specification. Static bindings are just the literal text, the attribute value from the HTML template.

Prefixing an attribute value with ognl: indicates that the value is really an OGNL expression, equivalent to using the <binding> element in the specification.

Finally, prefixing an attribute value with message: indicates that the value is really a key used to get a localized message, equivalent to the <message-binding> element in the specification. Every page, and every component, is allowed to have its own set of messages (stored in a set of .properties files), and the message: prefix allows access to the localized messages stored in the files.

[Tip]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).

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=". . ." href="#"> . . . </a>

This creates a conflict: will the template href be used, or the dynamically generated value 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.



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