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">
Hello,
<span jwcid="@Insert
" value="ognl:user.name">Joe User</span>
</span>
</body>
</html>
This is a reference to a declared component; the type and parameters of the component are in the page's specification. | |
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.
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).
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.
No components in discarded blocks | |
---|---|
If you put a component inside the body of an |
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.
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.
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. |
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.