tapestry-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From conflue...@apache.org
Subject [CONF] Apache Tapestry > Page Navigation
Date Mon, 21 Jan 2013 03:09: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/Page+Navigation">Page
Navigation</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>
        Moved to here the "Logical Page Names and URL Shortening" section from Component Templates
page, and fixed headers &amp; numbering<br />
    </div>
        <br/>
                         <h4>Changes (34)</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>{float:right|background=#eee}
<br></td></tr>
            <tr><td class="diff-changed-lines" >{contentbylabel:title=Related
<span class="diff-changed-words">Articles|showLabels=false|showSpace=false|space=@self|labels=request-processing<span
class="diff-added-chars"style="background-color: #dfd;">,rendering</span>}</span>
<br></td></tr>
            <tr><td class="diff-unchanged" >{float} <br> <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >* _component event_ requests target
a specific component on a specific page, triggering an event within that component <br>*
_render_ requests target a specific page, and stream the HTML markup for that page back to
the client <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">
<br>{float:right <br></td></tr>
            <tr><td class="diff-unchanged" >This dichotomy between component event
requests and render requests is new in Tapestry 5. It is in some ways based on ideas from
the Portlet specification and differentiating the two types of requests alleviates a number
of problems in traditional web applications related to the browser back button, or to the
user hitting the refresh button in their browser. <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h1.
Component Event Requests <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h2.
Logical Page Name Shortening <br></td></tr>
            <tr><td class="diff-unchanged" > <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">In
certain cases, Tapestry will shorten the the logical name of a page. For example, the page
class org.example.pages.address.CreateAddress will be given a logical name of &quot;address/Create&quot;
(the redundant &quot;Address&quot; is removed as a suffix). However, this only affects
how the page is referenced in URLs; the template file will still be CreateAddress.tml, whether
on the classpath, or as address/CreateAddress.tml (in the web context). <br> <br>{info}
<br>Tapestry actually creates multiple names for the name page:  address/Create and
address/CreateAddress are both synonymous. You can user either in Java code that refers to
a page by name, or as the page parameter of a PageLink. <br>{info} <br> <br>h2.
Component Event Requests &amp; Responses <br> <br></td></tr>
            <tr><td class="diff-unchanged" >Main Article: [Component Events] <br>
<br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >These URLs expose a bit of the internal
structure of the application. Over time, as an application grows and is maintained, the ids
of components may change. This means that component event request URLs should not be bookmarked.
Fortunately, users will rarely have the chance to do so (see below). <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Null response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
1. Null response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>If the event handler method
returns no value, or returns null, then the current page (the page containing the component)
will render the response. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >}{code} <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
String response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
2. String response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>When a string is returned,
it is expected to be the logical name of a page (as opposed to the page&#39;s fully qualified
class name). As elsewhere, the name of the page is case insensitive. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Again, a render request URL will be
constructed and sent to the client as a redirect. <br>{code}public String onAction(){
<br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;"> </span>
return &quot;Index&quot;; <br></td></tr>
            <tr><td class="diff-unchanged" >}{code} <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Class response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
3. Class response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>When a class is returned,
it is expected to be a page class. Returning a page class from an event handler is safer for
refactoring than returning a page name. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >As with other response types, a render
request URL will be constructed and sent to the client as a redirect. <br>{code}public
Object onAction(){ <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;"> </span>
return Index.class <br></td></tr>
            <tr><td class="diff-unchanged" >}{code} <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Page response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
4. Page response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>You may also return an
instance of a page, rather than the name or class of a page. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" > <br>public Object onAction(){
<br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;"> </span>
return index; <br></td></tr>
            <tr><td class="diff-unchanged" >}{code} <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
HttpError <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
5. HttpError <br></td></tr>
            <tr><td class="diff-unchanged" > <br>An event handler method
may return a [HttpError|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html]
instance to send an error response to the client. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >{code} <br>public Object onAction(){
<br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;"> </span>
return new HttpError(302, &quot;The Error message); <br></td></tr>
            <tr><td class="diff-unchanged" >}{code} <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Link response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
6. Link response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>An event handler method
may return a [Link|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html]
instance directly. The Link is converted into a URL and a client redirect to that URL is sent
to the client. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The [ComponentResources|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html]
object that is injected into your pages (and components) has methods for creating component
event and page render links. <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Stream response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
7. Stream response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>An event handler can also
return a [StreamResponse|http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html]
object, which encapsulates a stream to be sent directly to the client browser. This is useful
for components that want to, say, generate an image or PDF and provide it to the client. <br>
<br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
URL response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
8. URL response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>A java.net.URL response
is handled as a client redirect to an external URL. (In Tapestry 5.3.x and earlier this only
works for non-Ajax requests.) <br> <br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Object response <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
9. Object response <br></td></tr>
            <tr><td class="diff-unchanged" > <br>Any other type of object
returned from an event handler method is an error. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">h<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">1</span><span
class="diff-added-chars"style="background-color: #dfd;">2</span>.</span> Page
Render Requests <br></td></tr>
            <tr><td class="diff-unchanged" > <br>Render requests are simpler
in structure and behavior than component event requests. In the simplest case, the URL is
simply the logical name of the page. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The activate event handler may also
return a value, which is treated identically to a return value of a component event request
event trigger. This will typically be used in an access validation scenario. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">h<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">1</span><span
class="diff-added-chars"style="background-color: #dfd;">2</span>.</span> Page
Navigation Patterns <br></td></tr>
            <tr><td class="diff-unchanged" > <br>This combination of action
links and context and page context can be put together in any number of ways. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >Let&#39;s take a typical master/detail
relationship using the concept of a product catalog page. In this example, the ProductListing
page is a list of products, and the ProductDetails page must display the details for a specific
product. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.</span>
<span class="diff-added-words"style="background-color: #dfd;">h3. Pattern 1:</span>
Component event requests / Persistent Data <br></td></tr>
            <tr><td class="diff-unchanged" > <br>In this pattern, the ProductListing
page uses action events and a persistent field on the ProductDetails page. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >* The URL does not indicate the identity
of the product; if the user bookmarks the URL and comes back later, they will trigger the
previous case (no valid product id). <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-deleted-words"style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.</span>
<span class="diff-added-words"style="background-color: #dfd;">h3. Pattern 2:</span>
Component Event Requests / No Persistent Data <br></td></tr>
            <tr><td class="diff-unchanged" > <br>We can improve the previous
example without changing the ProductListing page, using a passivation and activation context
to avoid the session and make the links more bookmarkable. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >It has the advantage that the connection
from page to page occurs in type-safe Java code, inside the onActionFromSelect method of ProductListing.
It has the disadvantage that clicking a link requires two round trips to the server. <br>
<br></td></tr>
            <tr><td class="diff-deleted-lines" style="color:#999;background-color:#fdd;text-decoration:line-through;">h2.
Render Requests Only <br></td></tr>
            <tr><td class="diff-added-lines" style="background-color: #dfd;">h3.
Pattern 3: Render Requests Only <br></td></tr>
            <tr><td class="diff-unchanged" > <br>This is the most common
version of this master/detail relationship. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
            <tr><td class="diff-unchanged" >The setProductId() method is no longer
needed. <br> <br></td></tr>
            <tr><td class="diff-changed-lines" ><span class="diff-changed-words">h<span
class="diff-deleted-chars"style="color:#999;background-color:#fdd;text-decoration:line-through;">2</span><span
class="diff-added-chars"style="background-color: #dfd;">3</span>.</span> Limitations
<br></td></tr>
            <tr><td class="diff-unchanged" > <br>As your application&#39;s
workflow expands, you may find that there is not a reasonable way to avoid storing some data
persistently between requests, outside of the page activation context. For example, if from
the ProductDetails page, the user is allowed to navigate to related pages and then back to
ProductDetails, it starts to become necessary to keep passing that product id around from
page to page to page. <br></td></tr>
            <tr><td class="diff-snipped" >...<br></td></tr>
    
            </table>
    </div>                            <h4>Full Content</h4>
                    <div class="notificationGreySide">
        <h1><a name="PageNavigation-PageNavigation"></a>Page Navigation</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/Page+Life+Cycle">Page
Life Cycle</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/Page+Navigation">Page
Navigation</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>
            </table>
</div>

<p>In essence, a Tapestry application is a number of related pages, working together.
To some degree, each page is like an application unto itself.</p>

<p>Any individual request will be targeted at a single page. Requests come in two forms:</p>

<ul>
	<li><em>component event</em> requests target a specific component on a
specific page, triggering an event within that component</li>
	<li><em>render</em> requests target a specific page, and stream the HTML
markup for that page back to the client</li>
</ul>


<p>{float:right<br/>
This dichotomy between component event requests and render requests is new in Tapestry 5.
It is in some ways based on ideas from the Portlet specification and differentiating the two
types of requests alleviates a number of problems in traditional web applications related
to the browser back button, or to the user hitting the refresh button in their browser.</p>

<h2><a name="PageNavigation-LogicalPageNameShortening"></a>Logical Page
Name Shortening</h2>

<p>In certain cases, Tapestry will shorten the the logical name of a page. For example,
the page class org.example.pages.address.CreateAddress will be given a logical name of "address/Create"
(the redundant "Address" is removed as a suffix). However, this only affects how the page
is referenced in URLs; the template file will still be CreateAddress.tml, whether on the classpath,
or as address/CreateAddress.tml (in the web context).</p>

<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td>Tapestry actually
creates multiple names for the name page:  address/Create and address/CreateAddress are both
synonymous. You can user either in Java code that refers to a page by name, or as the page
parameter of a PageLink.</td></tr></table></div>

<h2><a name="PageNavigation-ComponentEventRequests%26Responses"></a>Component
Event Requests &amp; Responses</h2>

<p>Main Article: <a href="/confluence/display/TAPESTRY/Component+Events" title="Component
Events">Component Events</a></p>

<p>Component event requests may take the form of hyperlinks (<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/EventLink.html"
class="external-link" rel="nofollow">EventLink</a> or <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/ActionLink.html"
class="external-link" rel="nofollow">ActionLink</a>) or form submissions (<a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/corelib/components/Form.html"
class="external-link" rel="nofollow">Form</a>).</p>

<p>The value returned from an <a href="/confluence/display/TAPESTRY/Component+Events"
title="Component Events">event handler method</a> controls the response sent to the
client web browser.</p>

<p>The URL for a component event request identifies the name of the page, the nested
id of the component, and the name of the event to trigger on the component (this is usually
"action"). Further, a component event request may contain additional context information,
which will be provided to the event handler method.</p>

<p>These URLs expose a bit of the internal structure of the application. Over time,
as an application grows and is maintained, the ids of components may change. This means that
component event request URLs should not be bookmarked. Fortunately, users will rarely have
the chance to do so (see below).</p>

<h3><a name="PageNavigation-1.Nullresponse"></a>1. Null response</h3>

<p>If the event handler method returns no value, or returns null, then the current page
(the page containing the component) will render the response.</p>

<p>A page render link for the current page is created and sent to the client as a client
side redirect. The client browser will automatically submit a new request to generate the
page.</p>

<p>The user will see the newly generated content in their browser. In addition, the
URL in the browser's address bar will be a render request URL. Render request URLs are shorter
and contain less application structure (for instance, they don't include component ids or
event types). Render requests URLs are what your users will bookmark. The component event
request URLs are transitory, meaningful only while the application is actively engaged, and
not meant to be used in later sessions.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> <span
class="code-object">Object</span> onAction(){
  <span class="code-keyword">return</span> <span class="code-keyword">null</span>;
}</pre>
</div></div>

<h3><a name="PageNavigation-2.Stringresponse"></a>2. String response</h3>

<p>When a string is returned, it is expected to be the logical name of a page (as opposed
to the page's fully qualified class name). As elsewhere, the name of the page is case insensitive.</p>

<p>Again, a render request URL will be constructed and sent to the client as a redirect.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> <span
class="code-object">String</span> onAction(){
  <span class="code-keyword">return</span> <span class="code-quote">"Index"</span>;
}</pre>
</div></div>

<h3><a name="PageNavigation-3.Classresponse"></a>3. Class response</h3>

<p>When a class is returned, it is expected to be a page class. Returning a page class
from an event handler is safer for refactoring than returning a page name.</p>

<p>As with other response types, a render request URL will be constructed and sent to
the client as a redirect.</p>
<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java"><span class="code-keyword">public</span> <span
class="code-object">Object</span> onAction(){
  <span class="code-keyword">return</span> Index.class
}</pre>
</div></div>

<h3><a name="PageNavigation-4.Pageresponse"></a>4. Page response</h3>

<p>You may also return an instance of a page, rather than the name or class of a page.</p>

<p>A page may be injected via the <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/annotations/InjectPage.html"
class="external-link" rel="nofollow">InjectPage</a> annotation.</p>

<p>Often, you will configure the page in some way before returning the page (examples
below).</p>

<p>You can also return a component within the page, but this will generate a runtime
warning (unless you are doing a partial-page update via <a href="/confluence/display/TAPESTRY/Ajax+and+Zones"
title="Ajax and Zones">Ajax</a>).</p>


<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">@InjectPage
<span class="code-keyword">private</span> Index index;

<span class="code-keyword">public</span> <span class="code-object">Object</span>
onAction(){
  <span class="code-keyword">return</span> index;
}</pre>
</div></div>

<h3><a name="PageNavigation-5.HttpError"></a>5. HttpError</h3>

<p>An event handler method may return a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/services/HttpError.html"
class="external-link" rel="nofollow">HttpError</a> instance to send an error response
to the client.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> <span class="code-object">Object</span>
onAction(){
  <span class="code-keyword">return</span> <span class="code-keyword">new</span>
HttpError(302, "The Error message);
}</pre>
</div></div>

<h3><a name="PageNavigation-6.Linkresponse"></a>6. Link response</h3>

<p>An event handler method may return a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/Link.html"
class="external-link" rel="nofollow">Link</a> instance directly. The Link is converted
into a URL and a client redirect to that URL is sent to the client.</p>

<p>The <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/ComponentResources.html"
class="external-link" rel="nofollow">ComponentResources</a> object that is injected
into your pages (and components) has methods for creating component event and page render
links.</p>

<h3><a name="PageNavigation-7.Streamresponse"></a>7. Stream response</h3>

<p>An event handler can also return a <a href="http://tapestry.apache.org/current/apidocs/org/apache/tapestry5/StreamResponse.html"
class="external-link" rel="nofollow">StreamResponse</a> object, which encapsulates
a stream to be sent directly to the client browser. This is useful for components that want
to, say, generate an image or PDF and provide it to the client.</p>

<h3><a name="PageNavigation-8.URLresponse"></a>8. URL response</h3>

<p>A java.net.URL response is handled as a client redirect to an external URL. (In Tapestry
5.3.x and earlier this only works for non-Ajax requests.)</p>

<h3><a name="PageNavigation-9.Objectresponse"></a>9. Object response</h3>

<p>Any other type of object returned from an event handler method is an error.</p>

<h2><a name="PageNavigation-PageRenderRequests"></a>Page Render Requests</h2>

<p>Render requests are simpler in structure and behavior than component event requests.
In the simplest case, the URL is simply the logical name of the page.</p>

<p>Pages may have an <em>activation context</em>. The activation context
represents persistent information about the state of the page. In practical terms, the activation
context is usually the id of some database-persistent object.</p>

<p>When a page has an activation context, the values of the context are appended to
the URL path.</p>

<p>Not all pages have an activation context.</p>

<p>The activation context may be explicitly set when the render request link is created
(the PageLink component has a context parameter for this purpose). When no explicit activation
context is provided, the page itself is queried for its activation context.</p>

<p>This querying takes the form of an event trigger. The event name is "passivate" (as
we'll see shortly, there's a corresponding "activate"). The return value of the method is
used as the context. For example:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
<span class="code-keyword">public</span> class ProductDetail
{
  <span class="code-keyword">private</span> Product product;

  . . .

  <span class="code-object">long</span> onPassivate() { <span class="code-keyword">return</span>
product.getId(); }
}
</pre>
</div></div>

<p>The activation context may consist of a series of values, in which case the return
value of the method should be an array or a List.</p>

<div class='panelMacro'><table class='infoMacro'><colgroup><col width='24'><col></colgroup><tr><td
valign='top'><img src="/confluence/images/icons/emoticons/information.gif" width="16"
height="16" align="absmiddle" alt="" border="0"></td><td>Note: If you are using
the <a href="/confluence/display/TAPESTRY/Hibernate+User+Guide" title="Hibernate User Guide">tapestry-hibernate</a>
integration library and your passivate context is a Hibernate entity, then you can just use
the entity itself, not its id. Tapestry will automatically extract the entity's id into the
URL, and convert it back for the "activate" event handler method.</td></tr></table></div>

<h2><a name="PageNavigation-Pageactivation"></a>Page activation</h2>

<p>When a page render request arrives, the page is activated before it is rendered.</p>

<p>Activation serves two purposes:</p>

<ul>
	<li>It allows the page to restore its internal state from data encoded into the URL
(the activation context discussed above).</li>
	<li>It provides coarse approach to validating access to the page.<br/>
The later case, validation, is generally concerned with user identity and access; if you have
pages that may only be accessed by certain users, you may use the page's activate event handler
responsible for verifying that access.</li>
</ul>


<p>A page's activate event handler mirrors its passivate handler:</p>

<div class="code panel" style="border-width: 1px;"><div class="codeContent panelContent">
<pre class="code-java">
  . . .

  void onActivate(<span class="code-object">long</span> productId)
  {
     product = productDAO.getById(productId);
  }

  . . .
</pre>
</div></div>

<p>Here's the relevant part: when the page renders, it is likely to include more component
event request URLs (links and forms). The component event requests for those links and forms
will <em>also</em> start by activating the page, before performing other work.
This forms an unbroken chain of requests that include the same activation context.</p>

<p>To some degree, this same effect could be accomplished using a <a href="/confluence/display/TAPESTRY/Persistent+Page+Data"
title="Persistent Page Data">persistent page value</a>, but that requires an active
session, and the result is not bookmarkable.</p>

<p>The activate event handler may also return a value, which is treated identically
to a return value of a component event request event trigger. This will typically be used
in an access validation scenario.</p>

<h2><a name="PageNavigation-PageNavigationPatterns"></a>Page Navigation
Patterns</h2>

<p>This combination of action links and context and page context can be put together
in any number of ways.</p>

<p>Let's take a typical master/detail relationship using the concept of a product catalog
page. In this example, the ProductListing page is a list of products, and the ProductDetails
page must display the details for a specific product.</p>

<h3><a name="PageNavigation-Pattern1%3AComponenteventrequests%2FPersistentData"></a>Pattern
1: Component event requests / Persistent Data</h3>

<p>In this pattern, the ProductListing page uses action events and a persistent field
on the ProductDetails page.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductListing.html</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  &lt;t:loop source=<span class="code-quote">"products"</span> value=<span
class="code-quote">"product"</span>&gt;
    &lt;a t:type=<span class="code-quote">"actionlink"</span> t:id=<span
class="code-quote">"select"</span> context=<span class="code-quote">"product.id"</span>&gt;${product.name}&lt;/a&gt;
  &lt;/t:loop&gt;
</pre>
</div></div>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductListing.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  @InjectPage
  <span class="code-keyword">private</span> ProductDetails details;

  <span class="code-object">Object</span> onActionFromSelect(<span class="code-object">long</span>
productId)
  {
    details.setProductId(productId);

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

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductDetails.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  @Inject
  <span class="code-keyword">private</span> ProductDAO dao;

  <span class="code-keyword">private</span> Product product;

  @Persist
  <span class="code-keyword">private</span> <span class="code-object">long</span>
productId;

  <span class="code-keyword">public</span> void setProductId(<span class="code-object">long</span>
productId) { <span class="code-keyword">this</span>.productId = productId; }

  void onActivate()
  {
    product = dao.getById(productId);
  }
</pre>
</div></div>

<p>This is a minimal approach, perhaps good enough for a prototype.</p>

<p>When the user clicks a link, the component event request URL will initially be something
like "http://.../productlisting.select/99" and the final render request URL will be something
like "http://.../productdetails". Notice that the product id ("99") does not appear in the
render request URL.</p>

<p>It has some minor flaws:</p>

<ul>
	<li>It requires a session (to store the productId field between requests).</li>
	<li>It may fail if the ProductDetails page is accessed before a valid product id is
set.</li>
	<li>The URL does not indicate the identity of the product; if the user bookmarks the
URL and comes back later, they will trigger the previous case (no valid product id).</li>
</ul>


<h3><a name="PageNavigation-Pattern2%3AComponentEventRequests%2FNoPersistentData"></a>Pattern
2: Component Event Requests / No Persistent Data</h3>

<p>We can improve the previous example without changing the ProductListing page, using
a passivation and activation context to avoid the session and make the links more bookmarkable.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductDetails.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  @Inject
  <span class="code-keyword">private</span> ProductDAO dao;

  <span class="code-keyword">private</span> Product product;

  <span class="code-keyword">private</span> <span class="code-object">long</span>
productId;

  <span class="code-keyword">public</span> void setProductId(<span class="code-object">long</span>
productId) { productId = productId; }

  void onActivate(<span class="code-object">long</span> productId)
  {
    <span class="code-keyword">this</span>.productId = productId;

    product = dao.getById(productId);
  }

  <span class="code-object">long</span> onPassivate() { <span class="code-keyword">return</span>
productId; }
</pre>
</div></div>

<p>This change ensures that the render request URL will include the product id, i.e.,
"http://.../productdetails/99".</p>

<p>It has the advantage that the connection from page to page occurs in type-safe Java
code, inside the onActionFromSelect method of ProductListing. It has the disadvantage that
clicking a link requires two round trips to the server.</p>

<h3><a name="PageNavigation-Pattern3%3ARenderRequestsOnly"></a>Pattern 3:
Render Requests Only</h3>

<p>This is the most common version of this master/detail relationship.</p>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductListing.html</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  &lt;t:loop source=<span class="code-quote">"products"</span> value=<span
class="code-quote">"product"</span>&gt;
    &lt;a t:type=<span class="code-quote">"pagelink"</span> page=<span
class="code-quote">"productdetails"</span> context=<span class="code-quote">"product.id"</span>&gt;${product.name}&lt;/a&gt;
  &lt;/t:loop&gt;
</pre>
</div></div>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductListing.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
No code is needed to support the link.
</pre>
</div></div>

<div class="code panel" style="border-width: 1px;"><div class="codeHeader panelHeader"
style="border-bottom-width: 1px;"><b>ProductDetails.java</b></div><div
class="codeContent panelContent">
<pre class="code-java">
  @Inject
  <span class="code-keyword">private</span> ProductDAO dao;

  <span class="code-keyword">private</span> Product product;

  <span class="code-keyword">private</span> <span class="code-object">long</span>
productId;

  void onActivate(<span class="code-object">long</span> productId)
  {
    <span class="code-keyword">this</span>.productId = productId;

    product = dao.getById(productId);
  }

  <span class="code-object">long</span> onPassivate() { <span class="code-keyword">return</span>
productId; }
</pre>
</div></div>

<p>The setProductId() method is no longer needed.</p>

<h3><a name="PageNavigation-Limitations"></a>Limitations</h3>

<p>As your application's workflow expands, you may find that there is not a reasonable
way to avoid storing some data persistently between requests, outside of the page activation
context. For example, if from the ProductDetails page, the user is allowed to navigate to
related pages and then back to ProductDetails, it starts to become necessary to keep passing
that product id around from page to page to page.</p>

<p>At some point, persistent values make more sense. Tapestry has several persistence
strategies available, including one that stores data in URL query parameters. See <a href="/confluence/display/TAPESTRY/Persistent+Page+Data"
title="Persistent Page Data">Persistent Page Data</a> for details.</p>
    </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/Page+Navigation">View
Online</a>
        |
        <a href="https://cwiki.apache.org/confluence/pages/diffpagesbyversion.action?pageId=22872120&revisedVersion=13&originalVersion=12">View
Changes</a>
            </div>
</div>
</div>
</div>
</div>
</body>
</html>

Mime
View raw message