tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Component Rendering
Date Thu, 06 Jun 2013 23:50:00 GMT
<html>
<head>
    <base href="https://cwiki.apache.org/confluence">
            <link rel="stylesheet" href="/confluence/s/2042/9/12/_/styles/combined.css?spaceKey=TAPESTRY&amp;forWysiwyg=true"
type="text/css">
    </head>
<body style="background: white;" bgcolor="white" class="email-body">
<div id="pageContent">
<div id="notificationFormat">
<div class="wiki-content">
<div class="email">
    <h2><a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Component+Rendering">Component
Rendering</a></h2>
    <h4>Page <b>edited</b> by             <a href="https://cwiki.apache.org/confluence/display/~bobharner">Bob
Harner</a>
    </h4>
        <div id="versionComment">
        <b>Comment:</b>
        Added Rendering Comments section at Howard's suggestion<br />
    </div>
        <br/>
                         <h4>Changes (1)</h4>
                                 
    
<div id="page-diffs">
                    <table class="diff" cellpadding="0" cellspacing="0">
    
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" > <br>Most render phase methods
should return void, to avoid unintentionally short circuiting other methods for the same phase.
<br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">
<br>h3. Rendering Comments <br> <br>Starting with version 5.3, Tapestry
can optionally emit rendering comments for all requests; these are comments such as &lt;!\-\-BEGIN
Index:loop (context:Index.tml, line 15)\-\-&gt; that can assist you in debugging markup
output on the client-side. This will significantly increase the size of the rendered markup,
but can be very helpful with complex layouts to determine which component was responsible
for which portion of the rendered page. <br> <br>Rendering comments are only available
when not running in [production mode|Configuration#tapestry.production-mode]. <br> <br>To
turn on rendering comments for all requests, set the [tapestry.component-render-tracing-enabled|Configuration#tapestry.component-render-tracing-enabled]
configuration symbol to &quot;true&quot;. <br> <br>To turn on rendering
comments only for a particular request, add the query parameter {{t:component-trace=true}}
to the URL: <br> <br>{code} <br>  http://www.example.com/myapp/mypage?t:component-trace=true
<br>{code} <br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="ComponentRendering-ComponentRendering"></a>Component
Rendering</h1>

<div class='navmenu' style='float:right; background:#eee; margin:3px; padding:3px'><table
class="tableview" width="100%">
            <tr><th style="padding: 3px 3px 3px 0px">Related Articles</th></tr>
                        <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Events+FAQ">Component
Events FAQ</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Request+Processing">Request
Processing</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Events">Component
Events</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Content+Type+and+Markup">Content
Type and Markup</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Component+Rendering">Component
Rendering</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Page+Navigation">Page
Navigation</a>
        
                                            </td>
        </tr>
                <tr>
            <td>
                                 <span class="icon icon-page" title=Page>Page:</span>
                         <a href="/confluence/display/TAPESTRY/Page+Life+Cycle">Page
Life Cycle</a>
        
                                            </td>
        </tr>
            </table>
</div> 

<p>Rendering of components in Tapestry 5 is based on a <em>state machine</em>
and a <em>queue</em> (instead of the tail recursion used in Tapestry 4). This
breaks the rendering process up into tiny pieces that can easily be implemented or overridden.
Don't worry, in practice, writing components requires a breathtakingly small amount of code.</p>

<h2><a name="ComponentRendering-RenderingPhases"></a>Rendering Phases</h2>

<p>The rendering of each component is divided into a number of phases, illustrated below.</p>


<p><span class="image-wrap" style=""><img src="/confluence/download/attachments/21792222/component-render-states.png?version=1&amp;modificationDate=1275697053000"
title="Component Render States" style="border: 0px solid black" /></span><br/>
Each of the orange phases (SetupRender, BeginRender, BeforeRenderBody, etc.) corresponds to
an annotation you may place on one or more methods of your class. The annotation directs Tapestry
to invoke your method as part of that phase.</p>

<p>Methods marked with these annotations are called <b>render phase methods</b>.</p>

<p>Your methods may be void, or return a boolean value. Returning a value can force
phases to be skipped, or even be re-visited. In the diagram, solid lines show the normal processing
path. Dashed lines are alternate flows that are triggered when your render phase methods return
false instead of true (or void).</p>

<p>Render phase methods may take no parameters, or may take a parameter of type <a
href="/confluence/display/TAPESTRY/DOM" title="DOM">MarkupWriter</a>. The methods
can have any visibility you like ... typically, package private is used, as this visibility
makes it possible to unit test your code (from within the same Java package) without making
the methods part of the component's <em>public</em> API.</p>

<p>These methods are <b>optional</b>, a default behavior is associated with
each phase.</p>

<p>The large number of phases reflects the use of <a href="/confluence/display/TAPESTRY/Component+Mixins"
title="Component Mixins">component mixins</a> which also plug into the render phases.
Several of the phases exist almost exclusively for mixins.</p>

<p>Generally, your code will use the SetupRender, BeginRender, AfterRender and CleanupRender
phases ... often just one or two of those.</p>

<p>Here's the source for a looping component that counts up or down between two values,
renders its body a number of times, and stores the current index value in a parameter:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.example.app.components;

<span class="code-keyword">import</span> org.apache.tapestry5.annotations.Parameter;
<span class="code-keyword">import</span> org.apache.tapestry5.annotations.AfterRender;
<span class="code-keyword">import</span> org.apache.tapestry5.annotations.SetupRender;

<span class="code-keyword">public</span> class Count
{
    @Parameter
    <span class="code-keyword">private</span> <span class="code-object">int</span>
start = 1;

    @Parameter(required = <span class="code-keyword">true</span>)
    <span class="code-keyword">private</span> <span class="code-object">int</span>
end;

    @Parameter
    <span class="code-keyword">private</span> <span class="code-object">int</span>
value;

    <span class="code-keyword">private</span> <span class="code-object">boolean</span>
increment;

    @SetupRender
    void initializeValue()
    {
        value = start;

        increment = start &lt; end;
    }

    @AfterRender
    <span class="code-object">boolean</span> next()
    {
        <span class="code-keyword">if</span> (increment)
        {
            <span class="code-object">int</span> newValue = value + 1;

            <span class="code-keyword">if</span> (newValue &lt;= end)
            {
                value = newValue;
                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
            }
        }
        <span class="code-keyword">else</span>
        {
            <span class="code-object">int</span> newValue = value - 1;

            <span class="code-keyword">if</span> (newValue &gt;= end)
            {
                value = newValue;
                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;

            }
        }

        <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
    }
}
</pre>
</div></div>

<p>Returning false from next() causes Tapestry to re-run the BeginRender phase, and
from there, re-render the component's body (this component does not have a template). Returning
true transitions to the CleanupRender phase.</p>

<p>Notice how Tapestry adapts to your methods, as marked with the annotations. It also
adapts in terms of parameters; the two annotated methods here did not perform any output,
so they did not need a MarkupWriter.</p>

<p>What's really mind blowing is that the template and body of a component will often
contain ... more components! That means that many different components will be in different
phases of their own state machine.</p>

<h3><a name="ComponentRendering-SetupRender"></a>SetupRender</h3>

<p>The SetupRender phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/SetupRender.html"
class="external-link" rel="nofollow">SetupRender</a>) is where you can perform any
one-time per-render setup for your component. This is a good place to read component parameters
and use them to set temporary instance variables.</p>

<h3><a name="ComponentRendering-BeginRender"></a>BeginRender</h3>

<p>The BeginRender phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/BeginRender.html"
class="external-link" rel="nofollow">BeginRender</a>) occurs at the start of the
rendering of the component. For components that render a tag, the start tag should be rendered
here (the close tag should be rendered inside the AfterRender phase). The component can also
prevent the template and/or body from being rendered by returning false.</p>

<p>Components may or may not have a template. If a component has a template, and the
template includes a &lt;body&gt; element, then the BeforeRenderBody phase will be
triggered (giving the component the option of rendering its body or not).</p>

<p>If a component does not have a &lt;body&gt; element in its template, then
the BeforeRenderBody phase is not triggered.</p>

<p>If a component does not have a template, but does have a body, the BeforeRenderBody
phase is still triggered.</p>

<p>If no methods are annotated with BeginRender, then no special output occurs during
this phase, but the template (if present) or body (if no template is present, but the component
has a body) will be rendered.</p>

<h3><a name="ComponentRendering-BeforeRenderTemplate"></a>BeforeRenderTemplate</h3>

<p>The BeforeRenderTemplate phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/BeforeRenderTemplate.html"
class="external-link" rel="nofollow">BeforeRenderTemplate</a>) exists to allow a
component to decorate its template (creating markup around the template generated markup),
or to allow a component to skip its template.</p>

<h3><a name="ComponentRendering-BeforeRenderBody"></a>BeforeRenderBody</h3>

<p>The BeforeRenderBody phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/BeforeRenderBody.html"
class="external-link" rel="nofollow">BeforeRenderBody</a>) is associated with a component's
body (the portion of its container's template that the component occupies). The BeforeRenderBody
phase allows the component the ability to skip the body, while still rendering the rest of
the component's template (if any).</p>

<p>If no methods are annotated with BeforeRenderBody, then the body will be rendered
by default. Again, this occurs when the &lt;body&gt; element of the component's template
is reached, or automatically if the component has no template (but the component does have
a body).</p>

<h3><a name="ComponentRendering-AfterRenderBody"></a>AfterRenderBody</h3>

<p>The AfterRenderBody phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/AfterRenderBody.html"
class="external-link" rel="nofollow">AfterRenderBody</a>) is executed after the body
is rendered; this only occurs for components with a body.</p>

<h3><a name="ComponentRendering-AfterRender"></a>AfterRender</h3>

<p>The AfterRender phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/AfterRender.html"
class="external-link" rel="nofollow">AfterRender</a>) complements BeginRender, and
is often used to render the close tag that matches the start tag rendered in the BeginRender
phase. In any case, the AfterRender phase can continue on to CleanupRender, or revert back
to BeginRender (as in our Count component example, above).</p>

<p>If no methods are annotated with AfterRender, then no special output occurs, and
the CleanupRender phase is triggered.</p>

<h3><a name="ComponentRendering-CleanupRender"></a>CleanupRender</h3>

<p>The CleanupRender phase (see @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/CleanupRender.html"
class="external-link" rel="nofollow">CleanupRender</a>) is the counterpart to SetupRender,
allowing final cleanup to occur.</p>

<h2><a name="ComponentRendering-UsingMethodNamesinsteadofAnnotations"></a>Using
Method Names instead of Annotations</h2>

<p>If you prefer to avoid using annotations on your methods, you may do so by providing
specific names for your methods. The required method name is the annotation name, with the
first character decapitalized: setupRender(), beginRender(), etc. As with annotated render
phase methods, Tapestry is flexible about visibility, return type and parameters.</p>

<p>Using this mechanism, the earlier example can be rewritten as:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">package</span> org.example.app.components;

<span class="code-keyword">import</span> org.apache.tapestry5.annotations.Parameter;

<span class="code-keyword">public</span> class Count
{
    @Parameter
    <span class="code-keyword">private</span> <span class="code-object">int</span>
start = 1;

    @Parameter(required = <span class="code-keyword">true</span>)
    <span class="code-keyword">private</span> <span class="code-object">int</span>
end;

    @Parameter
    <span class="code-keyword">private</span> <span class="code-object">int</span>
value;

    <span class="code-keyword">private</span> <span class="code-object">boolean</span>
increment;

    void setupRender()
    {
        value = start;

        increment = start &lt; end;
    }

    <span class="code-object">boolean</span> afterRender()
    {
        <span class="code-keyword">if</span> (increment)
        {
            <span class="code-object">int</span> newValue = value + 1;

            <span class="code-keyword">if</span> (newValue &lt;= end)
            {
                value = newValue;
                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;
            }
        }
        <span class="code-keyword">else</span>
        {
            <span class="code-object">int</span> newValue = value - 1;

            <span class="code-keyword">if</span> (newValue &gt;= end)
            {
                value = newValue;
                <span class="code-keyword">return</span> <span class="code-keyword">false</span>;

            }
        }

        <span class="code-keyword">return</span> <span class="code-keyword">true</span>;
    }
}
</pre>
</div></div>

<p>This style is a trade off: on the gain side, the code is <em>even</em>
simpler and shorter, and the method names will, by design, be more consistent from one class
to the next. The down side is that the names are very generic, and may in some cases, be less
descriptive than using annotated methods (<tt>initializeValue()</tt> and <tt>next()</tt>
are, to some eyes, more descriptive).</p>

<p>You can, of course, mix and match, using specifically named render phase methods
in some cases, and annotated render phase methods in other cases.</p>

<h2><a name="ComponentRendering-RenderingComponents"></a>Rendering Components</h2>

<p>Instead of returning true or false, a render phase method may return a component.
The component may have been injected via the @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/Component.html"
class="external-link" rel="nofollow">Component</a> annotation, or may have been passed
to the owning component as a parameter.</p>

<p>In any case, returning a component will queue that component to be rendered <b>before</b>
the active component continues rendering.</p>

<p>The component to render may even be from a completely different page of the application.</p>

<p>Recursive rendering of components is not allowed.</p>

<p>This technique allows the rendering of Tapestry pages to be <em>highly</em>
dynamic.</p>

<p>Returning a component instance does <b>not</b> short circuit method invocation
(as described below), the way returning a boolean would. It is possible that multiple methods
may return components (this is not advised &#8211; insanity may ensue).</p>

<h2><a name="ComponentRendering-AdditionalReturnTypes"></a>Additional Return
Types</h2>

<p>Render phase methods may also return <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Block.html"
class="external-link" rel="nofollow">Blocks</a>, <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Renderable.html"
class="external-link" rel="nofollow">Renderables</a> or <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/runtime/RenderCommand.html"
class="external-link" rel="nofollow">RenderCommands</a>.</p>

<p>The following component returns a Renderable in the BeginRender phase and skips the
BeforeRenderTemplate phase:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class OutputValueComponent
{
    @Parameter
    <span class="code-keyword">private</span> <span class="code-object">String</span>
value;
    
    <span class="code-object">Object</span> beginRender()
    {
        <span class="code-keyword">return</span> <span class="code-keyword">new</span>
Renderable()
        {
            <span class="code-keyword">public</span> void render(MarkupWriter
writer)
            {
                writer.write(value);
            }
        };
    }
}
</pre>
</div></div>


<h2><a name="ComponentRendering-MethodConflictsandOrdering"></a>Method Conflicts
and Ordering</h2>

<p>It is possible to have multiple methods that are annotated with the same render phase
annotation. This may include methods in the same class, or a mix of method defined in a class
and inherited from other classes.</p>

<h3><a name="ComponentRendering-MixinsBeforeComponent"></a>Mixins Before
Component</h3>

<p>When a component has <a href="/confluence/display/TAPESTRY/Component+Mixins" title="Component
Mixins">mixins</a>, then the mixins' render phase methods execute <em>before</em>
the component's render phase methods. If a mixin extends from a base class, the mixin's parent
class methods execute before the mixin subclass' render phase methods.</p>

<p>Exception: Mixins whose class is annotated with @<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/MixinAfter.html"
class="external-link" rel="nofollow">MixinAfter</a> are ordered <em>after</em>
the component, not before.</p>

<p>The order in which the mixins of a given class (@MixinAfter or mixins before) execute
is determined by the ordering constraints specified for the mixins. If no constraints are
provided, the order is undefined. See <a href="/confluence/display/TAPESTRY/Component+Mixins"
title="Component Mixins">Component Mixins</a> for more details.</p>

<h3><a name="ComponentRendering-ParentsbeforeChild"></a>Parents before Child</h3>

<p>Ordering is always parent-first. Methods defined in the parent class are always invoked
before methods defined in the child class.</p>

<p>When a sub-class overrides an render phase method of a base class, the method is
only invoked once, along with any other base class methods. The subclass can change the <em>implementation</em>
of the base class method via an override, but can't change the <em>timing</em>
of when that method is invoked. See <a href="https://issues.apache.org/jira/browse/TAPESTRY-2311"
class="external-link" rel="nofollow">TAPESTRY-2311</a>.</p>

<h3><a name="ComponentRendering-ReverseOrderingforAfterXXXandCleanupRender"></a>Reverse
Ordering for AfterXXX and CleanupRender</h3>

<p>The After_XXX_ phases exists to balance the Begin_XXX_ and Before_XXX_ phases. Often
elements will be started inside an earlier phase and then the elements will be ended (closed)
inside the corresponding After_XXX_ phase (with the body and template of the component rendering
between).</p>

<p>In order to ensure that operations occur in the correct, and natural order, the render
phase methods for these two stages are invoked in <em>reverse order</em>:</p>

<ul>
	<li>Subclass methods</li>
	<li>Parent class methods</li>
	<li>Mixin subclass methods</li>
	<li>Mixin parent class methods</li>
</ul>


<h2><a name="ComponentRendering-WithinaSingleClass"></a>Within a Single
Class</h2>

<p>Currently, rendering methods having the same annotation within a single class are
executed in alphabetical order by method name. Methods with the same name are ordered by number
of parameters. Even so, annotating multiple methods with the same rendering phase is not a
great idea.  Instead, just define one method, and have it call the other methods in the order
you desire.</p>

<h3><a name="ComponentRendering-ShortCircuiting"></a>Short Circuiting</h3>

<p>If a method returns a true or false value, this will short circuit processing. Other
methods within the phase that would ordinarily be invoked will not be invoked.</p>

<p>Most render phase methods should return void, to avoid unintentionally short circuiting
other methods for the same phase.</p>

<h3><a name="ComponentRendering-RenderingComments"></a>Rendering Comments</h3>

<p>Starting with version 5.3, Tapestry can optionally emit rendering comments for all
requests; these are comments such as &lt;!&#45;&#45;BEGIN Index:loop (context:Index.tml,
line 15)&#45;&#45;&gt; that can assist you in debugging markup output on the client-side.
This will significantly increase the size of the rendered markup, but can be very helpful
with complex layouts to determine which component was responsible for which portion of the
rendered page.</p>

<p>Rendering comments are only available when not running in <a href="/confluence/display/TAPESTRY/Configuration#Configuration-tapestry.productionmode">production
mode</a>.</p>

<p>To turn on rendering comments for all requests, set the <a href="/confluence/display/TAPESTRY/Configuration#Configuration-tapestry.componentrendertracingenabled">tapestry.component-render-tracing-enabled</a>
configuration symbol to "true".</p>

<p>To turn on rendering comments only for a particular request, add the query parameter
<tt>t:component-trace=true</tt> to the URL:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  http:<span class="code-comment">//www.example.com/myapp/mypage?t:component-trace=<span
class="code-keyword">true</span></span>
</pre>
</div></div>
    </div>
        <div id="commentsSection" class="wiki-content pageSection">
        <div style="float: right;">
            <a href="https://cwiki.apache.org/confluence/users/viewnotifications.action"
class="grey">Change Notification Preferences</a>
        </div>
        <a href="https://cwiki.apache.org/confluence/display/TAPESTRY/Component+Rendering">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=21792222&revisedVersion=11&originalVersion=10">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message