论坛首页 Java企业应用论坛

Tapestry 5 页面导航

浏览 5136 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-03-16  
Tapestry 5 页面导航
 
 
本文根据http://tapestry.apache.org/tapestry5/tapestry-core/guide/pagenav.html翻译整理过来,请高手指正,转载请注明出处!
 
 
本质上,Tapestry应用是许多相关的工作在一起的页面的集合。从某种程度来说,每一个页面就是一个应用。
 
任何单个的请求对应一个页面。请求来源于两种形式:
  • 动作请求(Action Requests)指向一个特定的页面组件,在组件内部触发一个事件。
  • 呈现请求(render requests)指向一个特定的页面,将页面的HTML标记流输出到客户端。
动作请求与呈现请求的划分是Tapestry 5 中新出现的概念,它的某些方面是基于Portlet规范的思想,区分出这两种类型的请求减轻了许多在传统web应用中当用户使用浏览器的后退按钮或点击刷新按钮时的问题。
 
动作请求
 
动作请求表现为超链接(ActionLink)或表单提交(Form)的形式。
这两种情况,事件处理方法返回的值控制着送往用户端web浏览器的响应。
 
动作请求的URL包含页面名字、组件的嵌套id和在组件中触发事件的名字(通常是"action")。此外,一个动作请求可以包含额外的上下文信息(context information),以提供给事件处理方法(与方法参数有关)。
 
这些URL暴露了一些应用的内部结构。随着时间的推移,一个应用在维护中组件的id可能会改变,这就是说动作请求的URL不能被收藏(存为书签)。幸运的是,用户很少有这种机会来改变组件的id(参见下文):
 
以下是动件请求事件处理方法(动作的监听方法)的不同返回说明。
 
Null response
 
如果一个事件处理方法没有返回值或者返回null,此时当前页面(包含该组件的页面)会呈现响应(即响应请求)。
当前页面的页面呈现链接被创建并重定向到客户端。客户端浏览器随后提交的将是一个产生新页面的新请求。用户将会在浏览器看到一个新产生的内容。地址栏里的URL将会是一个呈现请求的URLrender request URL)。呈现请求的URL简短且包含较少的应用结构信息(比如,他不包含组件id和事件类型)。呈现请求URL是可供用户收藏的。动作请求URL是暂时性的,仅当应用处于激活状态时有意义,这并不意味着可以用于随后的session
 
String response
 
当返回一个字符串时,此字符串应该是页面的逻辑名(而非页面的全类名)。另外,页面的名字不区分大小写。
其次,呈现请求的URL被构造并重定向到客户端。
 
Page response
 
我们也可以返回一个页面实例,而非一个页面名字。
一个页面可以通过InjectPage annotation被注入。
通常,我们会在页面返回前以某种方式设置页面(举例如下,即对页面属性对象赋值)。
你也可以返回页面内的一个组件,但些时将会产生一个运行时警告。
 
Link response
 
事件处理方法可以直接返回一个Link实例,Link 被转换为URL然后重定向URL到客户端。
ComponentResources对象可以被注入到页面或组件中,它的方法可以创建action page链接(它们实际上定义在ComponentResourcesCommon中)。
 
Stream response
 
事件处理方法也能返回一个StreamResponse对象,这个对象封装了一个送往客户端浏览器的流(stream)。这对把组件产生的imagePDF送往客户端是很有用的。
 
Object response
 
事件处理方法返回任何其他对象类型都是错误的。
 
页面呈现请求(Page Render Requests
 
呈现请求在结构与行为上要比动作请求简单。简单情况下,URL仅仅是页面的逻辑名。
 
页面可以有一个活化上下文(activation context)。活化上下文表现为页面状态的持久化信息。实际上,活化上下文通常是一些数据库持久化对象的id
 
当一个页面有活化上下文时,上下文的值被追加到URL路径中。并不是所有的页面都有活化上下文。
 
当呈现请求链接被创建时,活化上下文可以被显式设值(PageLink组件有一个context参数可以用来设值)。当没有提供显式活化上下文时,页面会自己查找它的活化上下文。
 
这种查找表现为事件的触发。事件名字为passivate(我们马上会看到它的一个相对应的名字activate)。方法返回的值被用做上下文。
 
For example:
public class ProductDetail
{
 private Product _product;
  
  . . .
  
  long onPassivate() { return _product.getId(); }   
}
 
活化上下文可以由一连串的值组成,即方法返回的是数组或List列表。
 
页面激活Page activation
 
当一个页面呈现请求到来时,页面会在它呈现前被激活。
激活主要为了两个目的:
  • 它允许页面把内部状态数据编码到URL中(上面讨论到的活化上下文)。
  • It provides coarse approach to validating access to the page.它提拱了粗略的方法来校验页面是否可被访问。
后者提到的校验,通常涉及到用户标识及访问;如果我们的页面仅允许特定的用户访问,就可以用页面的激活事件来负责这个访问的校验。
一个页面的激活事件处理器(activate event handler)对应着它的钝化事件处理器(passivate handler):
 . . .
  
  void onActivate(long productId)
 {
     _product = _productDAO.getById(productId);
 }
  
  . . .
在此相关的部分是:当页面呈现时,它可能包含更多的动作请求URL(链接与表单)。那些链接与表单的动作请求也一样通过激活页面开始,然后再执行其他的操作。这形成了一个不断的包含同样活化上下文请求链。
 
从某种程度上说,我们也可以使用持久化页面值(persistent page value)来完成相同的效果,但这种方法需要一个激活的sessionURL不可收藏。
 
激活事件处理器(方法)也可以返回一个值,它与动作请求的事件触发器返回的值是一样的。这通常用于访问校验场景。
页面导航模式(Page Navigation Patterns)
动件链接和上下文及页面上下文可以以许多方式组合在一起。
让我们通过一个产品分类页来介绍一个经典的主列表/详细页关系的例子。在这个例子中,ProductListing页面是产品的列表页,ProductDetails页是相应的显示特定产品的详细页。
 
动作请求/持久化数据
 
这种模式下,ProductListing页面使用动作事件,ProductDetails页面使用了持久化属性。
 
ProductListing.html:
  <t:loop source="products" value="product">
    <a t:type="actionlink" t:id="select" context="product.id">${product.name}</a>
  </t:loop>
ProductListing.java:
 @InjectPage
 private ProductDetails _details;
  
  Object onActionFromSelect(long productId)
 {
    _details.setProductId(productId);
    
    return _details;
 }
ProductDetails.java:
 @Inject
 private ProductDAO _dao;
  
  private Product _product;
  
  @Persist
 private long _productId;
  
  public void setProductId(long productId) { _productId = productId; }
  
  void onActivate()
 {
    _product = _dao.getById(_productId);
 }
 
以上代码极少,也许对原型来说已经足够好了。
当用户点击一个链接时,动作请求URL起初类似http://.../productlisting.select/99,然后最终的呈现请求的URL将会类似http://.../productdetails。注意产品id"99")并没有显示在呈现请求的URL中。
 
它还有一些次要的缺点:
  • 它需要一个session(用来在请求之间保存_productId属性)。
  • 如果ProductDetails页面在被访问前已设置了一个有效的产品id,就有可能失败(原有值丢失)。
  • URL并没有指出产品的标识,如果用户收藏了这个URL,随后他们会引发先前的情况(没有有效的产品id)。
动作请求/活化上下文(Action Requests / Persistent Data??)
 
我们可以使用passivation activation 上下文来避免使用session,使得链接更可收藏,从而在不改变ProductListing页面的情况下改进先前的实例。
 
ProductDetails.java:
 @Inject
 private ProductDAO _dao;
  
  private Product _product;
  
  private long _productId;
  
  public void setProductId(long productId) { _productId = productId; }
  
  void onActivate(long productId)
 {
    _productId = productId;
    
    _product = _dao.getById(_productId);
 }
  
  long onPassivate() { return _productId; }
 
这里的改变保证了呈现请求的URL包含产品id,如http://.../productdetails/99
 
此种方法的优点是页面到页面的连接发生在类型安全的Java代码中——ProductListing页面的onActionFromSelect方法。它也有不好的地方就是点击一个链接服务端需要两次往返过程(由两个页面完成这一请求)。
 
呈现请求一次
 
这是一件最通用的主列表/详细页的版本。
 
ProductListing.html:
<t:loop source="products" value="product">
    <a t:type="pagelink" page="productdetails" context="product.id">${product.name}</a>
</t:loop>
ProductListing.java:
不需要代码来处理链接。
 
ProductDetails.java:
 @Inject
 private ProductDAO _dao;
  
  private Product _product;
  
  private long _productId;
   
  void onActivate(long productId)
 {
    _productId = productId;
    
    _product = _dao.getById(_productId);
 }
  
  long onPassivate() { return _productId; }
 
不再需要setProductId()方法。
 
局限性
 
随着我们应用流程的扩展,我们可能会发现找不到一个合理的方式去避免在不同的请求间(在页面活化上下文之外)持久化某些数据。比如:如果从ProductDetails页面开始,允许用户导航到相关的一些页面然后又返回到ProductDetails页面,这就开始需要在页面到页面再到页面间保持传递的产品id
 
在某些方面,持久化值更有意义。随后,我们将会实现客户端持久化策略(client-side persistence strategy),它将对持久化数据进行编码,如产品id属性将自动的加入到查询参数中(和隐藏的表单域)。
 
   发表时间:2007-03-17  
good,希望共进步!
0 请登录后投票
   发表时间:2007-03-19  
兄弟,很不错的,继续努力呀!!
0 请登录后投票
   发表时间:2007-03-20  
Great!
学习中...
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics