| <!-- $Id$ --> |
| <!-- |
| Copyright 2004 The Apache Software Foundation |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <chapter id="components"> |
| <title>Tapestry Components</title> |
| <para> |
| Tapestry components are "black boxes" that are involved with both rendering |
| HTML responses and responding to HTTP requests. |
| </para> |
| <para> |
| A Tapestry component is defined by its specification. |
| The specification is an XML file that defines the type of the component, it parameters, |
| the template used by the component, any components embedded within it and |
| how they are 'wired up', and (less often) any assets used by the component. |
| </para> |
| <para> |
| At runtime, the specification is used to identify and instantiate a class |
| for the component. When the page containing the component is rendered, the |
| component will access its HTML template to find the static HTML and embedded |
| components it will render. |
| </para> |
| <section id="components.params"> |
| <title>Parameters and Bindings</title> |
| <para> |
| Tapestry components are designed to work with each other, |
| within the context of a page and application. The process of rendering a page |
| is largely about pulling information from a source into a component |
| and doing something with it. |
| </para> |
| <para> |
| For example, on a welcome page, a component might get the <varname>userName</varname> property from |
| the <varname>visit</varname> object and insert it into the HTML response. |
| </para> |
| <para> |
| Each component has a specific set of parameters. |
| Parameters have a name, a type and may be required or optional. |
| </para> |
| <para> |
| To developers experienced with Java GUIs, it may appear that Tapestry |
| component parameters are the same as JavaBeans properties. This is not completely true. |
| JavaBeans properties are set-and-forget; the designer sets a value |
| for the property using a visual editor and the value |
| is saved with the bean until it is used at runtime. |
| </para> |
| <para> |
| Parameters define the type of value needed, but not the actual value. |
| This value is provided by a special object called a binding. |
| The binding is a bridge between the component and the parameter value, |
| exposing that value to the component as it is needed. The reason for all this |
| is to allow pages, and the components within them, to be shared by many |
| concurrent sessions ... a major facet in Tapestry's strategy |
| for maintaining application scalability. |
| </para> |
| |
| <para> |
| When a component needs the value of one of its parameters, it must |
| obtain the correct binding, an instance of interface &IBinding;, |
| and invoke methods on the binding to get the value from the binding. |
| Additional methods are used with output parameters to update the binding property. |
| </para> |
| |
| <para> |
| In most cases, discussed in the <link linkend="components.connected-params">next section</link>, |
| Tapestry can hide the bindings from the developer. In effect, it automates the process |
| of obtaining the binding, obtaining the value from it, and assigning it to a JavaBean |
| property of the component. |
| </para> |
| |
| <para> |
| There are two types of bindings: static and dynamic. |
| Static bindings are read-only; the value for the binding is |
| specified in the component specification. |
| </para> |
| |
| <para> |
| Dynamic bindings are more prevalent and useful. A dynamic |
| binding uses a JavaBeans property name to retrieve the value when needed |
| by the component. The source of this data is a property of some component. |
| </para> |
| |
| <para> |
| In fact, dynamic bindings use |
| <link linkend="beans.property-path">property paths</link>, allowing |
| a binding to |
| 'crawl' deeply through an object graph to access the value it needs. |
| This frees the components from relying totally on the properties of their container, |
| instead they are free to access properties of more distant objects. |
| </para> |
| |
| </section> |
| |
| <section id="components.connected-params"> |
| <title>Connected Parameters</title> |
| |
| <para> |
| In most cases, a developer is not interested in bindings; an easier model for developers is one |
| in which Tapestry uses the parameters and bindings to set properties of the component automatically. |
| Starting in release 2.1, Tapestry includes this behavior, with some constraints and limitations. |
| </para> |
| |
| <para> |
| Part of the &spec.parameter; specification for a parameter |
| is the <emphasis>direction</emphasis>, which can be one of the following values: |
| </para> |
| |
| <variablelist> |
| |
| <varlistentry> |
| <term>in</term> |
| <listitem> |
| <para> |
| Input parameter; the value is drawn from the binding (if bound) |
| and applied to the corresponding component property just before rendering the component. |
| </para> |
| </listitem> |
| </varlistentry> |
| |
| |
| <varlistentry> |
| <term>form</term> |
| <listitem> |
| <para> |
| A parameter which matches the semantics of a form component. |
| The parameter is treated like an <literal>in</literal> |
| parameter when the page is rendering. |
| </para> |
| |
| <para> |
| When the form containing the component is submitted, |
| the connected property is read (after the component renders), |
| and the value applied to the parameter. |
| </para> |
| </listitem> |
| </varlistentry> |
| |
| <varlistentry> |
| <term>custom</term> |
| <listitem> |
| <para> |
| Tapestry does not try to connect the parameter with any property; the component |
| is responsible for accessing the binding and retrieving or setting values. |
| </para> |
| |
| <para> |
| This type must be used for any kind of output parameter, or for an input parameter |
| where the property may be accessed other than during the rendering of the component. |
| </para> |
| </listitem> |
| </varlistentry> |
| </variablelist> |
| |
| <note> |
| <title>Why aren't output parameters connectable?</title> |
| <para> |
| The problem is the timing of output parameters. Sometimes a parameter is only an output |
| parameter when the containing form is submitted (for example, any of the form related components). |
| Sometimes a parameter is output many times (for example, &Foreach;) while the component |
| renders. |
| </para> |
| |
| <para> |
| The latter case may always be handled as custom; the former case may be handled |
| in the future. |
| </para> |
| </note> |
| |
| <para> |
| Defining a parameter as direction <literal>in</literal> causes Tapestry to connect the parameter to the corresponding |
| property of the component. The parameter specification must identify the Java type of the property. |
| Properties must be read/write (they must have both |
| getter and setter methods). |
| </para> |
| |
| <para> |
| Tapestry will set properties from parameters just before rendering the component. After the component renders, |
| the parameters are |
| cleared; they are returned to inital values. Tapestry reads these initial values just before it sets the |
| properties the first time. This makes it very easy to set defaults for optional parameters: just provide a default value |
| for the correspoinding instance variable. |
| </para> |
| |
| <para> |
| If the property is connected to an invariant binding (a static or field |
| binding), then the property is set just once, and never cleared. |
| </para> |
| |
| |
| <para> |
| There are times when the parameter name can't be used as the property name. For example, |
| the &PageLink; component has a <literal>page</literal> parameter, the name of the page to link to. |
| However, all components already have a <literal>page</literal> |
| property, the &IPage; that ultimately contains them. The specification for the &PageLink; component |
| connects the <literal>page</literal> parameter to a property named <literal>targetPage</literal> instead. |
| </para> |
| |
| <para> |
| Defining a connected parameter as required means that the parameter must be bound |
| <emphasis>and</emphasis> the binding must provide a non-null value. A runtime exception |
| is thrown when a required parameter's binding yields a null value. |
| </para> |
| |
| <para> |
| The following examples show how to declare and use a parameter: |
| </para> |
| |
| <example> |
| <title>Connected Parameter - Specification</title> |
| <programlisting><![CDATA[ |
| |
| |
| <specification ...> |
| |
| <parameter name="color" direction="in" java-type="java.awt.Color"/> |
| |
| ... |
| ]]> |
| </programlisting> |
| </example> |
| |
| <example> |
| <title>Connected Parameter - Java Code</title> |
| <programlisting> |
| |
| public class ColorComponent extends &AbstractComponent; |
| { |
| private Color color = Color.RED; |
| |
| public Color getColor() |
| { |
| return color; |
| } |
| |
| public void setColor(Color color) |
| { |
| this.color = color; |
| } |
| |
| protected void renderComponent(&IMarkupWriter; writer, &IRequestCycle; cycle) |
| throws RequestCycleException |
| { |
| writer.begin("font"); |
| writer.attribute("color", ^RequestContext;.encodeColor(color); |
| |
| renderWrapped(writer, cycle); |
| |
| writer.end(); |
| } |
| |
| } |
| </programlisting> |
| </example> |
| |
| <para> |
| In this example, the component writes its content inside a <sgmltag class="starttag">font</sgmltag> |
| element, with the HTML color attribute set from the <literal>color</literal> parameter. &RequestContext; |
| includes a static convienience method for converting from a |
| <classname>Color</classname> object to an encoded color |
| that will be meaningful to a web browser. |
| </para> |
| |
| <para> |
| The parameter is optional and defaults to red if not specified (that is, if the parameter |
| is not bound). |
| </para> |
| |
| |
| <para> |
| At runtime, Tapestry will invoke <function>setColor()</function> first (if the <literal>color</literal> |
| parameter is bound). It will then invoke <function>renderComponent()</function>. Finally (even if |
| <function>renderComponent()</function> throws an exception) it will invoke <function>setColor()</function> |
| again, to restore it back to the default value, <literal>Color.RED</literal>. |
| </para> |
| |
| <para> |
| This code includes a defect: because the parameter is optional, there is nothing to prevent |
| it from being bound to null. |
| </para> |
| |
| </section> |
| |
| <section id="components.informal-parameters"> |
| <title>Formal vs. Informal Parameters</title> |
| <para> |
| Tapestry components have two types of parameters: formal and informal. |
| </para> |
| <para> |
| Formal parameters are parameters defined in the |
| <link linkend="spec.parameter">component specification</link>. |
| Each formal parameter has a specific |
| (case sensitive) name and may be required or optional. |
| </para> |
| <para> |
| In many cases, there is a one-to-one mapping between a Tapestry component and a specific |
| HTML tag. For example, &Body; and <sgmltag class="starttag">body</sgmltag>, |
| &Form; and <sgmltag class="starttag">form</sgmltag>, etc. In other cases, a Tapestry component |
| produces a known single HTML tag. For example, &ActionLink;, &DirectLink;, &PageLink; and &ServiceLink; |
| all produce an <sgmltag class="starttag">a</sgmltag> tag. |
| </para> |
| <para> |
| To support truly rich interfaces, it is often necessary to specify additional attributes |
| of the HTML tags; usually this means setting the <varname>class</varname> of a tag so as to |
| get visual properties from a stylesheet. In other cases, display attributes may be specified inline |
| (this is often the case with attributes related to display width and height, since |
| CSS support for these properties are inconsistent between the major HTML 4.0 browsers). |
| </para> |
| <para> |
| In theory, these components <emphasis>could</emphasis> define additional formal parameters for |
| each possible HTML attribute ... but there are a huge number of possible attributes, many of |
| which are specific to a particular browser. |
| </para> |
| <para> |
| Instead, Tapestry has the concept of an <emphasis>informal parameter</emphasis>. |
| This is an "additional" parameter, not specified |
| in the component's specification. In most cases, where informal parameters are allowed, they |
| are added as additional HTML attributes (there are a few special exceptions, such as the &Script; component). |
| </para> |
| <para> |
| Informal parameters do have some limitations. Informal parameters that conflict with the names of |
| any formal parameters, or with any of the HTML attributes generated directly by the component, are silently |
| ommitted. The comparison is case-insensitve. Thus, for a &DirectLink; component, you can not change |
| the <varname>href</varname> attribute, even if you supply a <varname>Href</varname> (or other variation) informal parameter. |
| </para> |
| <para> |
| Not all Tapestry components even allow informal parameters; this is explicitly |
| stated in the <link linkend="spec.component-specification">component specification</link>. |
| </para> |
| <note> |
| <title>Informal Parameters that are Assets</title> |
| <para> |
| Tapestry includes a special case when an informal parameter is actually an |
| <link linkend="components.assets">asset</link>. The URL for the asset is determined and that is the value |
| supplied for the attribute. |
| </para> |
| </note> |
| </section> |
| <section id="components.embedded"> |
| <title>Embedded Components</title> |
| <para> |
| Under Tapestry, it is common to define new components |
| by combining existing components. The existing components are |
| embedded in the containing component. This is always true at the top level; Pages, |
| which are still Tapestry components, always embed other Tapestry components. |
| </para> |
| <para> |
| Each embedded component has an <varname>id</varname> (an identifying string) |
| that must be unique within the containing component. |
| Every non-page component is embedded inside some other |
| component forming a hierarchy that can get quite deep |
| (in real Tapestry applications, some pages have components nested three to five levels deep). |
| </para> |
| <para> |
| In some cases, a component will be referenced by its id path. |
| This is a series of component ids separated by periods, representing a path from |
| the page to a specific component. The same notation as a property path |
| is used, but the information being represented is quite different. |
| </para> |
| <para> |
| For example, the id path <literal>border.navbar.homeLink</literal> |
| represents the component named <varname>homeLink</varname>, |
| embedded inside a component named <varname>navbar</varname>, |
| embedded inside a component named <varname>border</varname>, embedded inside some page. |
| </para> |
| <para> |
| Tapestry components are "black boxes". They have a set of parameters that may |
| be bound, but their internals, how they are implemented, are not revealed. |
| </para> |
| <para> |
| Primitive components may not embed other components, or even |
| have a template. Nearly all the built-in components are primitive; they are |
| building blocks for constructing more complex components. |
| </para> |
| <para> |
| Alternately, a component may be implemented using a template and embedded components. |
| In either case, the names, types or very existence of embedded components is private, |
| hidden inside the containing component's "black box". |
| </para> |
| </section> |
| |
| |
| <section id="components.html-template"> |
| <title>HTML Templates</title> |
| |
| <para>Nearly all Tapestry components combine static HTML |
| <footnote> |
| <para> |
| The current releases of Tapestry is specifically oriented around HTML. Some support for |
| non-HTML languages, such as XML, XHTML or WML is already present |
| and will be expanded in the future. |
| </para> |
| </footnote> |
| from a template with additional dynamic content |
| (some few components are just dynamic content). |
| Often, a Tapestry component embeds other Tapestry components. |
| These inner components are referenced in the containing component's template. |
| </para> |
| |
| <para> |
| One of the features of Tapestry is <emphasis>invisible instrumentation</emphasis>. In most |
| web application frameworks, converting a static HTML page into a usable template is a destructive process: |
| the addition of new tags, directives or even Java code to the template means that it will no |
| longer preview properly in a <acronym>WYSIWYG</acronym> editor. |
| </para> |
| |
| <para> |
| Tapestry templates are instrumented using a new HTML attribute, <varname>jwcid</varname>, to any existing |
| element. Elements with such attributes are recognized by Tapestry as being dynamic, and driven by |
| a Tapestry component, but a <acronym>WYSIWYG</acronym> editor will simply ignore them. Once a template |
| is instrumented, it may be worked on by |
| <link linkend="components.html-production">both the HTML producer and the Java developer</link>. |
| </para> |
| |
| <para> |
| Identifying a Tapestry component within |
| an HTML template |
| is accomplished by adding a <varname>jwcid</varname> attribute to a tag. |
| </para> |
| <informalexample> |
| <programlisting> |
| <<replaceable>any</replaceable> jwcid="<replaceable>component id</replaceable>" ... > <replaceable>body</replaceable> </<replaceable>any</replaceable>> |
| </programlisting> |
| |
| <para>or |
| </para> |
| |
| <programlisting> |
| <<replaceable>any</replaceable> jwcid="<replaceable>component id</replaceable>" ... /> |
| </programlisting> |
| </informalexample> |
| <para> |
| Most often, the HTML element chosen is <sgmltag class="starttag">span</sgmltag>, though (in fact) Tapestry |
| completely ignores the element chosen by the developer, except to make sure the open and close tags balance. |
| </para> |
| <para> |
| The parser used by Tapestry is relatively forgiving about case |
| and white space. Also, the component id (and any other attributes) can be enclosed in |
| double quotes (as above), single quotes, or be left unquoted. |
| </para> |
| <para> |
| You are free to specify additional |
| attributes. These attributes will become |
| <link linkend="components.informal-parameters">informal parameters</link> for the Tapestry component. |
| </para> |
| |
| <para> |
| The start and end tags for Tapestry components must balance properly. This includes cases where the |
| end tag is normally ommitted, such as <sgmltag class="starttag">input</sgmltag> elements. Either a closing |
| tag must be supplied, or the XML-style syntax for an empty element must be used (that is, |
| a slash just before the end of the tag). |
| </para> |
| |
| |
| <section id="component.html-template.localization"> |
| <title>Localizing sections of a template</title> |
| |
| <para> |
| Tapestry includes an additional template feature to assist with localization of a web application. |
| By specifying a <sgmltag class="starttag">span</sgmltag> element with a special attribute, |
| <varname>key</varname>, Tapestry will replace the entire |
| <sgmltag class="starttag">span</sgmltag> tag with a |
| <link linkend="components.localization.strings">localized string</link> for the component. |
| </para> |
| |
| |
| <para> |
| This construct takes one of two forms: |
| </para> |
| |
| <informalexample> |
| <programlisting> |
| <span key="<replaceable>key</replaceable>" ... > ... </span> |
| </programlisting> |
| <para>or</para> |
| <programlisting> |
| <span key="<replaceable>key</replaceable>" ... /> |
| </programlisting> |
| </informalexample> |
| |
| <para> |
| If only the <varname>key</varname> |
| attribute is specified, then the <sgmltag class="starttag">span</sgmltag> |
| is simply replaced with the localized string. However, if any additional attributes |
| are specified for the <sgmltag class="starttag">span</sgmltag> tag beyond |
| <varname>key</varname>, then |
| the <sgmltag class="starttag">span</sgmltag> tag will be part of the rendered HTML, with |
| the specified attributes. |
| </para> |
| |
| <para> |
| The upshot of this is that sections of the HTML template can be invisibly localized |
| simply by wrapping the text to be replaced inside a <sgmltag class="starttag">span</sgmltag> |
| tag. The wrapped text exists, once more, as sample text to be displayed |
| in a <acronym>WYSIWYG</acronym> editor. |
| </para> |
| |
| </section> <!-- component.html-template.localization --> |
| |
| <section id="component.html-template.body"> |
| <title>Components with Bodies</title> |
| |
| <para> |
| In Tapestry, individual components may have their own HTML templates. This is a very powerful |
| concept ... it allows powerful and useful components to be created with very little code. By contrast, |
| accomplishing the same using JSP tags requires either that all the HTML be output |
| from the JSP tag directly, or that the JSP tag use some additional framework, such as Velocity, |
| to enable the use of a template. In either case the JSP tag author will need to divide the code or template |
| into two pieces (before the body and after the body). Tapestry allows components to simply have |
| a single template, with a marker for where the body is placed. |
| </para> |
| |
| |
| <para> |
| During the rendering |
| of a page, Tapestry knits together the templates of the page and all the nested components to create |
| the HTML response sent back to the client web browser. |
| </para> |
| |
| |
| <informalexample> |
| <programlisting> |
| Container content <co id="component.html-template.body.container1"/> |
| |
| <span jwcid="component"> <co id="component.html-template.body.component"/> |
| |
| Body content <co id="component.html-template.body.content"/> |
| |
| </span> |
| |
| More container content <co id="component.html-template.body.container2"/> |
| </programlisting> |
| </informalexample> |
| |
| <calloutlist> |
| <callout arearefs="component.html-template.body.container1"> |
| <para> |
| This portion of the container content is rendered first. |
| </para> |
| </callout> |
| |
| <callout arearefs="component.html-template.body.component"> |
| <para> |
| The component is then rendered. It will render, possibly using its |
| own template. |
| </para> |
| </callout> |
| |
| <callout arearefs="component.html-template.body.content"> |
| <para> |
| The component controls <emphasis>if</emphasis>, <emphasis>when</emphasis> and |
| <emphasis>how often</emphasis> the body content from its container |
| is rendered. |
| </para> |
| |
| <para> |
| Body content can be a mix of static HTML and additional components. These |
| components are <emphasis>wrapped</emphasis> by the component, but are |
| <emphasis>embedded</emphasis> in the component's container. |
| </para> |
| </callout> |
| |
| <callout arearefs="component.html-template.body.container2"> |
| <para> |
| After the component finishes rendering, the remaining content |
| from the container is rendered. |
| </para> |
| </callout> |
| </calloutlist> |
| |
| |
| |
| <para> |
| The body listed above can be either static HTML or other Tapestry |
| components or both. Elements in the body of a component are |
| wrapped by the containing component. The containing component controls the |
| rendering of the elements it wraps in its body. For example, |
| the &Conditional; component may decide not to |
| render its body and the &Foreach; component may render |
| its body multiple times. |
| </para> |
| <para> |
| Not all Tapestry components should have a body. |
| For example, the &TextField; component creates an |
| <sgmltag class="starttag">input type=text</sgmltag> |
| form element and it makes no sense for it to contain anything else. |
| Whether a component allows a body (and wrap other elements), or whether |
| it discards it, is defined in the |
| <link linkend="spec.component-specification">component's specification</link>. |
| </para> |
| <para> |
| Tapestry includes a special component, &RenderBody;, |
| which is used to render the body content from a component's container. |
| It makes it easy to create components that wrap other components. |
| </para> |
| |
| </section> <!-- component.html-template.body --> |
| |
| |
| </section> <!-- components.html-template --> |
| |
| <section id="components.html-production"> |
| <title>Tapestry and HTML Production</title> |
| <para> |
| Tapestry is design to work in a large-scale environment, that typically features |
| two seperate teams: a "creative" team that produces HTML and a |
| "technical" team that produces Tapestry pages, components and Java code. |
| </para> |
| <para> |
| The division of skills is such that the creative team has virtually no knowledge of Java and |
| a minimal understanding of Tapestry, and the technical team has a limited understanding of HTML |
| (and tend to be color blind). |
| </para> |
| <para> |
| The typical workflow is that the technical team implements the application, using very minimal HTML |
| ... that is, minimal attention to layout, font size, colors, etc. Just enough to be sure that |
| the functionality of the application is there. |
| </para> |
| <para> |
| Meanwhile, the creative team is producing HTML pages of what the finished application will look like. |
| These pages are like snapshots of the HTML produced by the running application. |
| </para> |
| <para> |
| <emphasis>Integration</emphasis> is the process of merging these two views of the application together. |
| Primarily, this involves marking up tags within the HTML page with |
| <varname>jwcid</varname> attributes, |
| to indicate |
| to Tapestry which portions of the page are dynamic. In this way, the |
| page can be used as a Tapestry HTML template. These changes are designed to be invisible to a |
| <acronym>WYSIWYG</acronym> HTML editor. |
| </para> |
| |
| <para> |
| Tapestry includes a number of additional features to allow the HTML producers to continue |
| working on HTML templates, <emphasis>even after</emphasis> their initial efforts have been |
| integrated with the Java developer's code. |
| </para> |
| <section> |
| <title>Implicitly removed bodies</title> |
| <para> |
| In many cases, a component doesn't allow a body, but one may be present in the HTML template. |
| As usual, this is declared in |
| the <link linkend="spec.component-specification">component's specification</link>. |
| Tapestry considers that body to be a sample value, one which exists to allow the HTML producer |
| to verify the layout of the page using a WYSIWYG editor (rather than having to run the entire application). |
| Tapestry simply edits out the body at runtime. |
| </para> |
| <para>For example, an HTML producer may create an HTML template that includes a table |
| cell to display the user's name. The producer includes a sample value so that the |
| cell isn't empty (when previewing the HTML layout). |
| </para> |
| <informalexample> |
| <programlisting> |
| <td><span jwcid="insertName">John Doe</span></td> |
| </programlisting> |
| </informalexample> |
| <para> |
| The &Insert; component doesn't allow a body, so Tapestry edits out the |
| content of the <sgmltag class="starttag">span</sgmltag> tag from the HTML template. The fact that |
| a <sgmltag class="starttag">span</sgmltag> was used to represent the &Insert; component in the |
| HTML template is irrelevant to Tapestry; any tag could have been used, Tapestry just |
| cares that the start and end tags balance. |
| </para> |
| <para> |
| At runtime, Tapestry will combine the HTML template and the &Insert; component to produce the |
| final HTML: |
| </para> |
| <informalexample> |
| <programlisting> |
| <td>Frank N. Furter</td> |
| </programlisting> |
| </informalexample> |
| <para> |
| This editting out isn't limited to simple text; any HTML inside the body is removed. However, |
| none of that content may be dynamic ... the presence of a |
| <varname>jwcid</varname> attribute will cause a parsing exception. |
| </para> |
| </section> |
| <section> |
| <title>Explicitly removed bodies</title> |
| <para> |
| Another feature related to production and integration is the ability to remove sections of the HTML template. |
| Producers often include some optional portions on the page. The canonical example of this is a page that |
| shows a table of results; the HTML producer will usually include extra rows to demonstrate the look and layout of |
| a fully populated page. |
| </para> |
| <para> |
| The first row will be wrapped by a &Foreach; and otherwise changed to include dynamic links and output, but what about |
| the other rows? |
| </para> |
| <para> |
| To handle this case, |
| Tapestry recognizes a special <varname>jwcid</varname> attribute value: <literal>$remove$</literal>. |
| Using this special id causes |
| Tapestry to edit out the tag and all of its contents. Thus, each additional <sgmltag class="starttag">tr</sgmltag> in the |
| table should specify the value <literal>$remove$</literal> for attribute <varname>jwcid</varname>. |
| </para> |
| <informalexample> |
| <programlisting><![CDATA[ |
| <table> |
| <tr jwcid="foreach"> |
| <td><span jwcid="insertUserName">John Doe</span></td> |
| <td><span jwcid="insertAge">42</span></td> |
| </tr> |
| <tr jwcid="$remove$"> |
| <td>Frank N. Furter</td> |
| <td>47</td> |
| </tr> |
| <tr jwcid="$remove$"> |
| <td>Bob Doyle</td> |
| <td>24</td> |
| </tr> |
| </table>]]></programlisting> |
| </informalexample> |
| </section> |
| <section> |
| <title>Limiting template content |
| </title> |
| <para> |
| In a typical Tapestry application, some form of Border component provides a significant portion of every page. |
| This typically includes the outermost <sgmltag class="starttag">html</sgmltag>, <sgmltag class="starttag">head</sgmltag> and <sgmltag class="starttag">body</sgmltag> |
| tags, as well as <sgmltag class="starttag">table</sgmltag>s used to control layout. |
| </para> |
| <para> |
| In the static HTML pages from the creative team, this is not directly visible ... they <emphasis>must</emphasis> |
| include all the content |
| normally generated by the Border component in order to see what the HTML page actually looks like. |
| </para> |
| <para> |
| By default, the <emphasis>entire</emphasis> HTML template is the content for the page. |
| This causes a problem, even after a <sgmltag class="starttag">span</sgmltag> |
| is added, to represent the Border component ... much of the HTML is duplicated, |
| once from the static HTML, then dynamically from the Border component. |
| </para> |
| <para> |
| To eliminate this problem, Tapestry has a second special <varname>jwcid</varname> attribute: <literal>$content$</literal>. |
| Using this special id causes Tapestry to limit its view of the HTML template to just the content inside the tag. Anything outside |
| the defined content is completely ignored. |
| </para> |
| </section> |
| <section> |
| <title>Limits</title> |
| <para> |
| Ideally, the HTML pages created by the HTML producers would be used as is |
| as the HTML templates. Changes made for integration, the adding of <varname>jwcid</varname> attributes and such, |
| would be copied back into the HTML pages. |
| </para> |
| <para> |
| Given the use of the <literal>$remove$</literal> and <literal>$content$</literal> |
| <varname>jwcid</varname>'s, |
| this is practical |
| to a point. Once the application starts using a number of re-usable components, there |
| isn't a good way to perform the integration short of cutting and replacing |
| some of the HTML page content to form the HTML template. |
| </para> |
| </section> |
| </section> |
| <section id="components.localization"> |
| <title>Localization</title> |
| <para> |
| Tapestry has built in support for localization, designed to be easy to use. This localization support |
| is defined in terms of transforming the user interface into a format appropriate the the locale |
| of the user. This primarily takes the form of localized text (translated into the |
| end-user's language), but can also affect other aspects of look and feel including colors, images and layout. |
| </para> |
| |
| <para> |
| Tapestry has two different methods for supporting localization; developers are free to |
| mix and match solutions according to their own preferences. |
| </para> |
| |
| <para>Each client connecting to the application will select a particular <classname>Locale</classname>. |
| When a page for the application is created, the locale is used to select the correct localized |
| resources. |
| Locales are defined by the ISO (International Standards Organization). |
| A locale consists of a language code (such as 'en' for English, 'de' for German or 'fr' for French) |
| and a country code (such as 'AU' for Australia, 'BE' for Belguim, or 'GB' for United Kingdom). |
| </para> |
| |
| |
| <para> |
| A client's initial locale is determined by analyzing HTTP headers provided with the initial request. |
| An application may override this default, which records a client-side cookie identifyng the desired locale. |
| An example of this is included in the Tapestry Workbench demonstration. |
| </para> |
| |
| <section id="components.localization.strings"> |
| <title>Localization with Strings</title> |
| |
| <para> |
| Each individual component may have a set of <emphasis>localized strings</emphasis>. |
| Remember that pages are just a specific kind of component. |
| This set is built, much like the properties of a &ResourceBundle;, from one |
| or more <literal>.properties</literal> files. These files are located on the classpath, in the same |
| directory as the component specification (the <literal>.jwc</literal> file). |
| </para> |
| |
| <para> |
| The search for strings is much the same as with &ResourceBundle;, except |
| that only <literal>.properties</literal> files are considered (&ResourceBundle; also looks for classes). |
| </para> |
| |
| <para> |
| Example: for a component <filename>/com/skunkworx/skunkapp/Border.jwc</filename> and a locale of |
| <literal>fr_BE</literal> would be: |
| </para> |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border_fr_BE.properties</filename> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border_fr.properties</filename> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border.properties</filename> |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| |
| <para> |
| Searching for individual keys works just as with &ResourceBundle;, the search starts in the most |
| specific file (<literal>Border_fr_BE.properties</literal>) and continues downward if not found. |
| </para> |
| |
| |
| <para> |
| Components can gain access to their container's localized strings |
| via the &spec.string-binding; |
| element in the component specification. |
| </para> |
| |
| </section> |
| |
| <section id="components.localization.templates"> |
| <title>Localization with Templates</title> |
| |
| <para> |
| Tapestry allows multiple versions of HTML templates and assets (described in a |
| <link linkend="components.assets">later section</link>) |
| to be deployed with the application. |
| </para> |
| |
| <para> |
| The base template name is derived from the specification name, |
| by changing the <literal>jwc</literal> extension to <literal>html</literal>. |
| For example, component <filename>/com/skunkworx/skunkapp/Border.jwc</filename> |
| will have a base template name of <filename>/com/skunkworx/skunkapp/Border.html</filename>. |
| This resource name is used as the basis of a search that includes the locale. |
| Various suffixes are inserted just before the '.html' extension. |
| </para> |
| |
| <para> |
| A French speaking Belgian visitor would provoke the following search: |
| </para> |
| |
| <itemizedlist> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border_fr_BE.html</filename> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border_fr.html</filename> |
| </para> |
| </listitem> |
| <listitem> |
| <para> |
| <filename>/com/skunkworx/skunkapp/Border.html</filename> |
| </para> |
| </listitem> |
| </itemizedlist> |
| |
| |
| <note> |
| <para> |
| This form of localization actually predates the alternate form, |
| using <link linkend="components.localization.strings">localized strings</link>. |
| Localizing the strings seperately from the rest of the HTML template |
| is generally a better and easier way. Localization of templates |
| will, in the future, be used primarily when changing the layout of the template ... |
| for example, to provide a right-to-left orientation in a Hebrew |
| localization. |
| </para> |
| </note> |
| |
| </section> |
| |
| </section> <!-- components.localization --> |
| |
| <section id="components.assets"> |
| <title>Assets</title> |
| <para> |
| Assets are images (GIF, JPEG, etc.), movies, sounds or other collateral associated |
| with a web application. Assets come in three flavors: external, context and private. |
| </para> |
| <para> |
| External assets live at an arbitrary URL. Context assets use a URL within the |
| servlet context hosting the Tapestry application; |
| these assets are deployed |
| within the same Web Application Archive (WAR) as the application. |
| </para> |
| <para> |
| Private assets come from the Java classpath and are |
| resources not normally visible to the web server. |
| </para> |
| <para> |
| Tapestry uses the assets concept to address two areas: localization and deployment. |
| </para> |
| <para> |
| For localization: internal and private assets are localized, |
| just like HTML templates. That is, the path name provided is |
| used as the basis for a search that takes into account the desired locale. |
| External assets can't be localized in this way. |
| </para> |
| <para> |
| Private assets allow for easy deployment because the assets are |
| packaged with the HTML templates and Java code of the application, |
| inside a Java Archive (JAR) file. |
| </para> |
| <para> |
| Private assets support re-usability; a re-usable component may be |
| packaged with supporting assets (typically, image files) and used in any |
| Tapestry application without change, and without having to locate, extract or |
| otherwise fiddle with those assets. |
| </para> |
| <para> |
| The Tapestry framework provides two ways of exposing the assets to the client web browser. |
| </para> |
| <para> |
| First, it provides a service that will access the asset dynamically. |
| The URL encodes the application servlet and the resource to download, |
| and Tapestry framework code will pump the bytes down to the client web browser. |
| This is the default behavior (and is most useful during development). |
| </para> |
| <para> |
| The second method involves copying the asset out to a directory visible |
| to the web server, and creating a URL for it in its final location. |
| This requires some extra configuration of the application. |
| This method also has some implications when deploying new versions of the web application. |
| These are addressed later in this document. |
| </para> |
| </section> |
| <section id="components.helper-beans"> |
| <title>Helper Beans</title> |
| <para> |
| There is a second form of aggregation allowed with Tapestry components. The first way, covered previously, is to use embedded |
| components to extend the functionality of the outer component. In some cases, useful behavior can be isolated, |
| not into an additional component, but into a simple JavaBean. |
| </para> |
| <para> |
| These additional beans, called helper beans, are defined in the component specification, in the |
| &spec.bean; element. |
| Each bean has a unique name, a class to instantiate, |
| and a lifecycle (which controls how long the component keeps a reference to the bean). The specification |
| allows properties of the bean to be set as well, using |
| the &spec.set-property; and &spec.set-string-property; elements. |
| Helper beans are accessed through the <varname>beans</varname> property of the component. |
| </para> |
| <para> |
| Beans are created as needed, they may then be cached for future use according to their declared lifecycle. |
| The default lifecycle is <varname>request</varname>, meaning that the same bean will be returned until the end of the current request cycle. |
| </para> |
| <para> |
| An alternate lifecycle, <varname>page</varname>, means that once the bean is instantiated, it will continue to be available |
| for the lifetime of the page containing it. Remember that helper beans should never contain any client-specific |
| state, since a page will be used by multiple sessions and clients. |
| </para> |
| <para> |
| The last available lifecycle, <varname>none</varname>, indicates that the bean is not cached at all, and will be created fresh on |
| each property access. |
| </para> |
| |
| <para> |
| Tapestry includes a handful of useful helper beans. &Default; is used to provide default values for optional parameters. |
| &ValidationDelegate; |
| and several implementions of &IValidator; |
| used with &ValidField;, it allows simple handling of validation and presentating validation errors. |
| &EvenOdd; is used by the <link linkend="inspector">Tapestry Inspector</link>; it generates a stream of values alternating between "even" and "odd"; this is combined with cascading stylesheets to make the rows alternate between white and grey backgrounds. |
| </para> |
| </section> |
| |
| </chapter> |