Tapestry 5 页面导航
本质上,Tapestry应用是许多相关的工作在一起的页面的集合。从某种程度来说,每一个页面就是一个应用。
任何单个的请求对应一个页面。请求来源于两种形式:
- 动作请求(Action Requests)指向一个特定的页面组件,在组件内部触发一个事件。
- 呈现请求(render requests)指向一个特定的页面,将页面的HTML标记流输出到客户端。
动作请求与呈现请求的划分是Tapestry 5 中新出现的概念,它的某些方面是基于Portlet规范的思想,区分出这两种类型的请求减轻了许多在传统web应用中当用户使用浏览器的后退按钮或点击刷新按钮时的问题。
动作请求
这两种情况,事件处理方法返回的值控制着送往用户端web浏览器的响应。
动作请求的URL包含页面名字、组件的嵌套id和在组件中触发事件的名字(通常是"action")。此外,一个动作请求可以包含额外的上下文信息(context information),以提供给事件处理方法(与方法参数有关)。
这些URL暴露了一些应用的内部结构。随着时间的推移,一个应用在维护中组件的id可能会改变,这就是说动作请求的URL不能被收藏(存为书签)。幸运的是,用户很少有这种机会来改变组件的id(参见下文):
以下是动件请求事件处理方法(动作的监听方法)的不同返回说明。
Null response
如果一个事件处理方法没有返回值或者返回null,此时当前页面(包含该组件的页面)会呈现响应(即响应请求)。
当前页面的页面呈现链接被创建并重定向到客户端。客户端浏览器随后提交的将是一个产生新页面的新请求。用户将会在浏览器看到一个新产生的内容。地址栏里的URL将会是一个呈现请求的URL(render request URL)。呈现请求的URL简短且包含较少的应用结构信息(比如,他不包含组件id和事件类型)。呈现请求URL是可供用户收藏的。动作请求URL是暂时性的,仅当应用处于激活状态时有意义,这并不意味着可以用于随后的session。
String response
当返回一个字符串时,此字符串应该是页面的逻辑名(而非页面的全类名)。另外,页面的名字不区分大小写。
其次,呈现请求的URL被构造并重定向到客户端。
Page response
我们也可以返回一个页面实例,而非一个页面名字。
通常,我们会在页面返回前以某种方式设置页面(举例如下,即对页面属性对象赋值)。
你也可以返回页面内的一个组件,但些时将会产生一个运行时警告。
Link response
事件处理方法可以直接返回一个Link实例,Link
被转换为URL
然后重定向URL
到客户端。
Stream response
事件处理方法也能返回一个StreamResponse对象,这个对象封装了一个送往客户端浏览器的流(stream
)。这对把组件产生的image
或PDF
送往客户端是很有用的。
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(链接与表单)。那些链接与表单的动作请求也一样通过激活页面开始,然后再执行其他的操作。这形成了一个不断的包含同样活化上下文请求链。
激活事件处理器(方法)也可以返回一个值,它与动作请求的事件触发器返回的值是一样的。这通常用于访问校验场景。
页面导航模式(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);
}
以上代码极少,也许对原型来说已经足够好了。
它还有一些次要的缺点:
- 它需要一个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; }
此种方法的优点是页面到页面的连接发生在类型安全的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-16 23:50
- 浏览 5725
- 评论(3)
- 论坛回复 / 浏览 (3 / 5138)
- 查看更多
相关推荐
本文将使用Tapestry 5 创建一个CRUD(创建、读取、更新、删除)应用,展示Tapestry在页面导航、依赖注入、输入验证和状态管理等方面的使用。同时,还会介绍如何利用Tapestry的内置Ajax功能创建自定义组件。 开发...
这包括页面导航、依赖注入和资源注入、用户输入验证以及状态管理。Tapestry 还内置了Ajax支持,使得创建Ajax组件变得更加容易。 在开始使用Tapestry 5 开发之前,确保安装了Java SE Development Kit 5.0或更高版本...
Tapestry 5通过定义组件之间的链接关系来实现页面导航。这种机制不仅使得页面之间的跳转更加简洁明了,而且还可以轻松地管理和组织应用程序的结构。 ##### 3.2 依赖注入(Dependency Injection)与资源注入(Resource ...
- **页面和组件路径**:Tapestry 5使用声明式的方式映射URL到页面和组件,简化了路由和导航。 - **链接生成**:生成安全、语义化的链接是Tapestry 5的强项,它会自动处理URL参数和安全需求。 6. **国际化和本地化...
5. **事件处理和页面导航**: Tapestry支持事件驱动的编程模型。当用户与页面交互时,Tapestry会触发相应的事件,并调用处理方法。此外,页面之间的导航可以通过声明式的方式定义,使得应用的流程更加清晰。 6. **...
4. **编写第一个页面**:在Tapestry中,一个页面是由一个或多个组件组成的。你需要定义一个类来表示页面,并在HTML模板中声明组件。 接下来,我们转向"tapestry组件.docx",组件是Tapestry的核心组成部分,它们是可...
通过这个示例,读者可以学习到Tapestry中的页面导航、依赖注入、输入验证和状态管理等关键概念。此外,还将探讨如何利用Tapestry的内建Ajax支持创建自定义组件。 为了开始使用Tapestry 5,你需要安装Java SE ...
1. `<display-name>`标签设置了应用的显示名称,这里是"Tapestry 5 Tutorial"。 2. `<context-param>`定义了一个参数,`tapestry.app-package`参数指定了应用的主要包名,即`com.tapestry.tutorial`。这个包必须包含...
7. **URL路由与页面导航** `PageRenderRequestHandler`和`PageRedirector`接口处理URL到页面的映射和页面间的跳转,确保了URL的语义化和易于理解。 8. **错误处理与验证** Tapestry的异常处理机制允许开发者通过`...
5. **页面生命周期**:Tapestry定义了页面从初始化到销毁的完整生命周期。每个页面都有一个对应的Java类,该类在页面加载、提交表单等操作时被实例化和调用。 6. **切换皮肤**:在Tapestry中,可以轻松实现皮肤的...
1. **链接管理:** 使用Tapestry开发的应用程序无需显式地管理URL和HTTP请求之间的关系,框架自动处理页面之间的导航和状态管理。 2. **请求处理:** Tapestry关心页面、组件、事件等概念,开发者无需关注底层的HTTP...
Tapestry通过组件事件处理和页面导航规则来管理应用程序的流程,这使得开发者能够专注于业务逻辑,而不是繁琐的页面跳转和状态管理。 **Tapestry Spindle插件的功能** 1. **代码提示与补全**:Tapestry Spindle...
3. **页面导航**:讨论页面之间的导航控制,如URL映射、重定向和转发。 4. **错误和异常处理**:如何捕获和处理运行时错误,以及定义自定义的错误页面。 5. **性能优化**:提供性能调优的建议,如缓存策略、页面编译...
页面导航通常是无状态的,通过URL参数或隐式对象实现。 4. **类型安全的链接和表单** 与传统Web框架不同,Tapestry的链接和表单是类型安全的。这意味着在生成链接或处理表单提交时,Tapestry会自动绑定请求参数到...
在Tapestry4的开发过程中,路由和页面导航也是一个关键部分。框架自动处理URL到页面组件的映射,使得开发者可以轻松地创建链接和表单提交,而无需编写复杂的URL解析代码。同时,Tapestry4支持页面级和应用程序级的...
Tapestry的类型安全URL映射使得开发者能够清晰地定义页面之间的导航,避免了常见的URL编码错误。 Spring框架的核心是依赖注入(DI),它允许通过配置文件或注解来管理对象的生命周期和依赖关系。这样,对象之间的...
`IEngine`是Tapestry的核心组件,负责整个应用的生命周期管理和页面导航。 总的来说,Tapestry通过组件化、模型-视图-控制器的设计以及强大的OGNL表达式,为Java开发者提供了一种高效、灵活的Web应用开发工具。通过...
对于状态管理,Tapestry提供了一种机制,可以在服务器端持久化组件状态,即使在页面刷新或导航后也能恢复。 6. **性能优化**: 由于Tapestry生成的页面是服务器端渲染的,因此它可以进行一些性能优化,如延迟加载...