Tapestry开发指南0.8
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Tapestry开发指南
数字签名人 董黎伟
辨别名:CN = 董黎伟, C =
CN-中国, O = 成都麦柯系统
集成有限公司, OU = 技术开
发二部
原因:我是该文档的作者
位置:四川成都
日期:2005.01.26 16:41:38
+08'00'
Tapestry技术论坛:/viewforum.php?f=17
我的Email:gzdlw_Adong@
(技术问题请在论坛发贴)
1, Tapestry所处的状况
1)为什么需要web框架?
多数情况下,跨不同的应用来重用商业组件是困难的,因为由商业组件提供的功能在其它应用里不需要:它可能是唯一针对你的应用的。
然而,应用服务可以在机构跨应用重用,跟跨机构一样好,因为它们提供的服务对不同类型的应用都有用。
这些应用服务可能包括请求路由(对一个MVC结构),错误处理,以通用的方式产生的客户端脚本和已准备好的标签库。
2) Tapestry所处的状况
以下是各种web框架比较: (by Matt Raible)
struts Spring
MVC WebWork Tapestry JSF
优点广泛流行;拥有
很多信息和示
例;HTML
标
签库是一个非
常好的东西。
生命周期由重
写绑定,效验等
确定;能够无缝
隙地与各种表
现层程序结合
在一起,如
JSP,XSL等;
IC使得很容易
测试。
结构简单,容易
被扩展;标签库
容易被自定义,
并获得Velocity
支持;拦截机制
成熟可靠。
一旦学会,效率
很高;HTML
模板很合适美
工;有良好的社
区支持。
J2EE规范;能
够快速和容易
地开发;丰富的
导航框架。
缺点ActionForms非
常恶心;不能够
做单元测试;大
量的邮件列表
令人无法忍受
很少被使用;需
要在JSP中写
入大量代码;太
灵活了,反而不
能得到通用的
控制。
其文档最近才
出现,示例很
少;客户端效验
很不成熟。
文档非常概念,
而缺少实践;学
习曲线陡峭,示
例非常少;
标签依赖
JSPs;技术不够
成熟;实现资源
不单纯。
Matt Raible认为:
如果是快速却低技术含量的项目,他会选择struts,因为struts比较成熟;
如果是高技术含量的企业级项目,他会选择Tapestry,因为Tapestry可重用组件;
如果是开源项目,他会选择WebWork,因为使用WebWork需要对框架有更深入的认识。
2, Tapestry目前在我们的项目中所处的状况:
我们的成都建信的网站部分全部是用Tapestry做的。
在过去的两个月中,我们已经解决了Tapestry的大部分技术问题。
我们可以在论坛上随处可见,大部分人对Tapestry的学习曲线问题都有顾虑。
追究其根本,原因在于:Tapestry的理念颠覆了传统的web应用层框架的理念。
其实所谓传统,无非就是struts 和JSP,因为struts应用最为广泛,而JSP几乎算是所有JA V A表现层框架的本源。
Tapestry也是源于JSP。
我不可否认,任何web表现层都可以用JSP来实现。
就如同任何java程序都可以使用J2SDK 来编写。
但是,需要非常关注的是,我们如果真这样做,的确对程序员来说“最简单”,但是却牺牲了工作效率。
这绝对是得不偿失的。
目前成都建信网站部分,基本上是冰箱哥哥和我搭档。
我负责网站表现层,而冰箱哥哥负责向表现层提供业务逻辑。
随着网站表现层部分的不断开发,我们发现一个不可否认的事实,随着项目功能实现越多,表现层的工作越显得轻松。
因为大量重复使用的Tapestry组件,以及高度复用的表现层逻辑,使得工作效率大幅度提升。
就如同Hibernate使得我们数据库业务方面的工作效率大幅提升一样,Tapestry使得我们在工作上的“轻松”和“快捷”也同样不可忽视。
3,为什么要使用Tapestry ?
Tapestry的灵魂就四个字:基于组件。
我们来看个示例:
这是我们现在正在使用的分页方式,
表现层和业务层联系的纽带是:int preNo, int page, String parameter这三个参数。
当实现翻页功能的时候,我们实际上是控制这三个参数实现数据库查询。
表现层有非常多的分页功能,但是在翻页的时候,因为要传递三个参数,甚至可能传递更多的参数,比如说,在网站的法律法规中,我们不光要传法规的栏目别名,还要穿法规的范围别名。
因此,实际上表现层和业务层是一种“千丝万缕”的关系。
我们无法相互独立,我们甚至不知道,在新的分页功能要求到来时,需要传递怎样的参数。
现在我有一个假设,如果表现层,直接抛给业务层一个PagingAndSorting对象,我们
现在,我们会发现,对于表现层来说,不同的分页实际上都是控制同一个对象PagingAndSorting;对于业务层来说,不同的业务功能实际上也是控制同一个对象PagingAndSorting。
这是不是有一点像“搭积木”?既然我们传递的是对象,可以说“想要啥子,就传啥子”。
这就是一个变相的“基于组件”理念的应用。
是的,实际上,所谓“基于元素”和“基于组件”,与我们常说的“面向过程”和“面向对象”是相同的思维方式,只是应用环境不同罢了。
Tapestry就是目前比较主流的两种“基于组件”的表现层框架之一,另外一种“基于组件”表现层框架是JSF,但是JSF还不够成熟。
Tapestry组件是一个“黑盒子”,用于表现HTML响应,以及响应HTTP请求。
Tapestry组件通过其规范定义。
规范是一个XML文档,其中定义了组件类型,参数,组件模板,包含的组件以及被包含组件之间的联系,还有所有的assets。
在运行的时候,规范指定并实例一个类,当页面所包含的组件被表现的时候,组件将访问HTML模板,找到静态的HTML并植入组件。
使用Tapestry我们能够获得以下好处:
1)能够保证对HTML最少限度的干扰,也就是说,美工和表现层程序员有了非常明确的分工。
Tapestry对HTML页面的介入可以仅仅是增加一个jwcid属性。
2)基于组件,使得我们的表现层变成由一个个“积木”堆积而成。
Page规范就是这些积木之间的纽带。
而java文件担当了处理逻辑的角色。
3)由于表现层逻辑全部放在了java文件里面,使得我们对逻辑的操作达到了“随心所欲”的地步。
4)随着我们项目的深入,我们积累的各种积木也就越来越多,也就说,我们的工作越来越轻松,工作效率越来越高。
以上4个特点,都是JSP所不能够带给我们的。
尤其是工作效率的提升。
当然,实际上Tapestry
所带给我们的远不至这些好处。
我们都知道,作为程序员,即便我们工作如何的严谨,仍然不能够避免BUG的问题。
因为不论我们如何“仔细”,但是在编程过程中,仍然无法解决一些隐蔽的“陷阱”,甚至有些“陷阱”直到暴露之前,我们都还没有意识到其存在。
现在有了Tapestry 这层“保险”,虽然我们不可否认,由于对Tapestry的熟练程度不足,可能会给我们带来新的“陷阱”,但是Tapestry为我们解决的“陷阱”远比带给我们的“陷阱”更多更隐蔽。
Tapestry学起来困难吗?我说不难.struts简单吗?我说不简单.平心而论,一个完全不懂JSP 的人学会struts并实用到项目中,需要多长时间?我觉得,保守估计两个月吧.如果有同仁的技术指导,一个月差不多.现在,我们有现成的项目正在使用Tapestry开发,也不缺乏“第一个吃螃蟹”的同仁。
所以我有足够的理由认为,学以致用Tapestry,一个月足已.
在这里,我不打算讨论各种框架的优势劣态。
所谓尺有所长,寸有所短。
我只阐述怎样使用Tapestry,但是在开始之前,我希望能够达成某种默契:
第一:虽然我用Tapestry实现了一些功能,但是我想,这些实现方式未必就是最佳的,所以,如果你对我某些做得不足之处,非常欢迎您指教。
☺
第二:我在论坛上见过有人提如此不智的问题:“我觉得struts的标签很好用,Tapestry和WebWork有没有类似的东西?”如果Tapestry都和struts一样了,那么还何必存在Tapestry呢?
第三:我打算分三章来讲解Tapestry,每章的开头我都会有个小小的建议.我觉得这些建议对Tapestry的学习是有帮助的。
以便更容易的颠覆已有思维。
第四:在使用Tapestry的过程中,也许会觉得某些功能,如果使用JSP来完成会简单得多。
对此我希望能够保持一个冷静的心态。
俗语又云:“打架看后半场”。
OK,让我们现在来“火拼”Tapestry吧☺
第一章:熟悉常用组件,习惯组件思维.
建议:请抛弃并忘记JSP。
第一节:基础概念
1 概述:
Tapestry是一个全面web application框架,是使用JA V A写的。
Tapestry不是一个application server,Tapestry是一个使用在application server中的框架。
Tapestry不是一个application,Tapestry是一个用来创建web application的框架。
Tapestry不是JSP的一种使用方式,Tapestry和JSP只能够选择一种。
Tapestry不是一个脚本环境,Tapestry使用一种组件对象模式(component object model),这并不是一种简单的脚本,而是用于生成高动态性高互交性的web页面。
Tapestry基于Java Servlet API version 2.2,兼容于JDK 1.2以上版本,Tapestry通过变换多样的组件模式,将一个web application分离为一个联合组件。
每一个组件都拥有其特殊的责任用于显示web页面或者响应HTML请求。
2 Tapestry工作原理
Tapestry应用程序由几个页面组成,这些页面都是由独立的,可重复使用,可配置的组件组成。
下面是用于描述Tapestry应用程序的基本术语:
3 页面(Page):应用程序由一堆命名唯一的页面组成,每个页面有一个模板和若干组件;
4 模板(Template):一个用于页面(或一个组件)的HTML模板。
Tapestry中,一个模板
包括基本的HTML markup,以及一些用于标记组件的特殊属性的标签。
5 组件(Component):用于Tapestry页面的可重复使用的对象。
当一个页面表现时,或者
页面中的一个链接被触发时,组件产生相应的HTML代码。
多个组件也可以用来构成一个新的组件。
6 参数(Parameter):组件拥有一些参数,用于组件属性与页面属性之间的连接。
组件通
常读取自己的参数,但是一些组件(与HTML forms相关)能够更新自己的参数,并且
更新与参数绑定的页面属性。
3 Tapestry与MVC
Tapestry组件扮演着控制器Controller的角色,是模式层(Model)中pure-domain objects 和包含有组件的HTML模板之间的媒介。
大多数情况下,这种方式应用于页面(页面也是Tapestry组件),但是在某些情况中,一个组件拥有自己的模板,包含着更多的组件,并且支持与使用者的互交。
页面通过配置一系列属性表达式(Property expressions)连接模式层和表现层。
属性表达式使用另外一种开源框架OGNL(Object Graph Navigation Language)。
OGNL的开源工程(project)独立于Tapestry,但是在Tapestry中起很重要的作用。
OGNL主要的目的在于读取和更新对象的Java Bean属性。
4 markup和domain object。
1)mockup:page mockups是静态HTML页面,用于表现这些动态页面在应用程序运行时的样子。
也就是指在HTML模板中,将会在应用程序运行时被Tapestry组件替换掉的那部分旧
HTML代码。
Tapestry组件是动态的,当对HTML模板做美工时,markup的存在将会提供很大的方便。
这样,Tapestry程序员可以完全与美工人员各负其责。
2)domain object:应用程序的运行,最终取决于整个团队中JAVA部分的构架师和程序员。
在大多数应用程序中,怎样连接用户接口和domain objects成为一个问题。
Domain objects是中间层对象,是应用层,在整个应用程序中,它们是全局对象,将数据保存到数据库,或者实现你的特殊业务。
通常,我们涉及到这些问题:这些对象中储存着什么信息,怎样将不同的对象关联在一起,以及它们怎样读取数据,或着将数据储存到数据库。
servlet作为控制器,收到请求。
定位并更新domain objects,读取或更新数据库数据。
控制器servlet选择一个表现层(JSP)表现响应。
表现层绘制domain objects并最终将响应页面发送客户端。
5 页面结构:
在Tapestry应用程序中,一个页面(page)由一个HTML模块,一个页面规范(page specification),和一个JAVA页面类(page class)构成。
每个Tapestry页面有一个特殊的唯一的名称。
页面名称被用来定位页面规范和HTML模板。
页面规范的一部分用来实例化JAVA类,这部分称为页面类(page class),包括指定应用程序中的一些特殊属性和方法。
表现(rendering)页面的第一步是实例化页面。
Tapestry框架读取页面规范和HTML模板并生成页面实例。
一个Tapestry页面不是一个单一的对象。
页面对象是树对象的根对象,这些对象包括页面模板中的组件,HTML模板中的内容,以及一些用来连接分散区域的对象。
最简单的页面:
1)一个HTML模板;
2)一个页面规范;
该规范使用XML,必须声明:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE page-specification
PUBLIC "-//Apache Software Foundation//Tapestry Specification 3.0//EN"
"/tapestry/dtd/Tapestry_3_0.dtd">
<page-specification class="com.cdmcs.tapestry.page.MessageBoard">
</page-specification>
3)一个页面类;
该类必须继承BasePage类。
public abstract class HomePage extends BasePage {
}
只要HTML模板中使用Tapestry,就必须声明页面规范和页面类,即便页面规范和页面
类都没有任何属性或方法,变量。
6 关于属性标签:
a) jwcid属性:(Java Web Component ID)在模板中用来指定组件。
b) <span>标签:HTML<span>标签是一个用来包容text和elements的容器,其本身并不能
显示任何内容,仅仅是作为一个stylesheet协助对页面显示的控制。
c) @记号:用来标明一个隐式组件。
7 属性指定机制(specified properties)
属性指定就是由Tapestry自动生成典型的JavaBean属性。
在代码中,定义抽象方法用来读取
和更新属性,只需定义即将使用的方法。
Tapestry自己会生成一个子类来实现你的方法。
你甚至不用声明变量,只需要在页面规范中指明类型即可。
如:
<page-specification class="com.cdmcs.tapestry.page.MessageBoard">
<property-specification name="guestBookService"
type="com.cdmcs.oa.service.GuestBookService">
global.springContext.getBean("guestBookService")
</property-specification>
<property-specification name="item"type="com.cdmcs.bo.Persistable"/>
</page-specification>
在这里,我们定义了两个属性(property)。
一个属性命名为:guestBookService,它的类型是:GuestBookService,并且我们将一个global.springContext.getBean("guestBookService")的实例赋给了它。
两一个属性命名为:item,类型是:Persistable。
在MessageBoard类里面,当我们需要调用这两个属性的时候,仅仅需要调用该属性的抽象JavaBean方法,而不需要定义与初始化实例。
如:
public abstract GuestBookService getGuestBookService();
/**判断是否有资格进入留言板 */
public boolean isAccessDisable() {
return getGuestBookService().isBooleanIPForbid(this.getUserIP());
}
实际上,Tapestry会自动创建一个子类来继承MessageBoard类,并实现以JavaBean方式实现变量。
这样做有三个好处:
第一:减轻程序员负担;
第二:当请求失效过期的时候,Tapestry可以确保自动重置属性。
第三:属性可以被定义为persistent。
这种机制与EJB的container-managed persistence(CMP)类似。
8 组件的分类:
按照组件的使用方式:
a) 隐式组件:组件类型和其结构直接在HTML模板中申明的组件。
通常,Tapestry已经定
义好的组件都是以隐式组件的方式使用。
b) 显示组件:其组件类型和结构储存在页面规范中。
通常,自定义的组件都是以显示组件
的方式使用。
实际上Tapestry的组件逻辑比较复杂,再加上OGNL表达式和属性指定机制,使得写注释都变得很不容易。
所以在阅读别人写的Tapestry代码的时候,难免有雾里看花,尤抱琵琶半遮面的感觉。
我的看法是,假如有看不懂的代码,暂时死记硬背先。
因为Tapestry是一个功能强大的框架,其组件的可重复使用(reusable)能力非常强。
通常例子程序的页面类中的某一个方法,就已经能够解决与此方法相关的一系列问题。
第二节:实战
成都建信网有个留言板,该留言板有三个功能:
第一:进入留言板后,列出已有的所有留言;
第二:实现留言功能,也就是说,要有表单提交功能;
第三:对每一个留言,都有回复功能,回复只能针对特定留言。
Tapestry实现留言板功能,需要三个文件:MessageBoard.html,MessageBoard.page和MessageBoard.java。
我们怎样将这三个文件联系在一起呢?首先,在web-inf文件夹下有一个athena.application。
该文件指定了html文件与page文件之间的联系:
<page name="MessageBoard"specification-path="/WEB-INF/MessageBoard.page"/> 我们实际上还有另外一种方法,可以指定page文件到HTML模板的映射,在page文件中可以添加如下一句:
<context-asset name="$template"path="cdcin/Home.html"/>
打开MessageBoard.page文件,我们可以发现,声明Tapestry规范之后的第一件事,就是指定page 文件所指向的java文件:
<page-specification class="com.cdmcs.tapestry.page.MessageBoard">
一,列出所有留言:
为了实现留言板的第一个功能,列出数据库中所有关于留言的数据,页面如图:
图 1
我们可以看到,是多个
图 2
的循环。
下面我们来研究一下,怎样实现这个功能:
先看HTML模板:
<span jwcid="@Foreach"source="ognl:messageList"value="ognl:item"
index="ognl:foreachIndex">
<table width="100%"border="0"cellspacing="0"cellpadding="0">
<tr>
<td width="177"height="77"valign="top"
background="images/hfk_1.gif"><table width="100%"border="0"
cellspacing="0"cellpadding="0">
<tr>
<td width="81"height="19"> </td>
<td width="32"> </td>
<td> </td>
</tr>
<tr>
<td height="32"> </td>
<td width="32"height="32"><img jwcid="@Image"
image="ognl:getAsset('image' + item.head)"alt="头像"
src="images/messageBoard/1.gif"width="32"height="32"border="0"/></td> <td> </td>
</tr>
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
</table></td>
<td width="17"background="images/hfk_2.gif"> </td>
<td width="21"background="images/hfk_3.gif"> </td>
<td valign="top"background="images/hfk_4.gif"><table
width="100%"border="0"cellspacing="0"cellpadding="0">
<tr>
<td> </td>
<td> </td>
<td> </td>
</tr>
<tr align="left">
<td height="30"colspan="3"><img src="images/icon/b1.gif" width="15"height="15"/>留言主题:<span jwcid="@Insert"
value="ognl:item.title"/></td>
</tr>
<tr>
<td height="30"colspan="3"><a jwcid="@GenericLink"
href="ognl:'http://' + item.homepage"
disabled="ognl:item.homepage==null"><img src="images/icon/home.gif"
width="16"height="16"align="absmiddle"border="0"/>主页
</a> <a jwcid="@GenericLink"
href="ognl:'mailto:' + item.email "disabled="ognl:item.email==null"><img src="images/icon/mail.gif"width="16"height="16"align="absmiddle" border="0"/>电子邮件</a> <a
jwcid="@ExternalLink"page="MessageBoard"parameters='ognl:new
ng.Object[]{new ng.Integer("3"), item.id}'
disabled="ognl:item.replyType==2"><img src="images/icon/replay.gif"
width="16"height="16"align="absmiddle"border="0"/>回复</a></td>
</tr>
</table></td>
<td width="23"background="images/hfk_6.gif"> </td>
</tr>
</table>
<table width="100%"border="0"cellspacing="0"cellpadding="0">
<tr>
<td width="23"height="50"
background="images/bl_5.gif"> </td>
<td align="center"valign="top"><table width="100%"
height="100%"border="0"cellpadding="0"cellspacing="0">
<tr>
<td width="154"valign="top"><table width="154"border="0" cellspacing="0"cellpadding="0">
<tr>
<td width="68"height="22"align="center">姓 名</td>
<td width="86"><span jwcid="@Insert"
value="ognl:ername"/></td>
</tr>
<tr>
align="center"
<td height="22">性 别</td>
<td><span jwcid="@Conditional"
condition='ognl:item.sex.equals("1")'>先生</span>
<span jwcid="@Conditional"
condition='ognl:item.sex.equals("0")'>女士</span>
</td>
</tr>
<tr>
align="ce
<td height="22"nter">工作单位</td>
<td><span jwcid="@Insert"value="ognl:item.job"/>
</td>
</tr>
<tr>
<td height="22"align="center">联系电话</td>
<td><span jwcid="@Insert"
value="ognl:item.telephone"/> </td>
</tr>
<tr>
align="ce
<td height="22"nter">日期时间</td>
<td><span jwcid="@Insert"
value="ognl:item.messageDate"/> </td>
</tr>
</table></td>
<td width="17"background="images/hfk_8.gif"> </td>
<td valign="top"><table width="100%"border="0"
cellspacing="0"cellpadding="0">
<tr>
<td width="20"height="15"> </td>
<td align="left"><span jwcid="@Insert"
value="ognl:@com.cdmcs.util.FormatString@formatNewline(item.content)" raw="true">内容文字</span></td>
<td width="20"> </td>
</tr>
<tr>
<td> </td>
<td valign="top"><table width="100%"border="0" cellspacing="2"cellpadding="0">
<span jwcid="@Foreach"source="ognl:writeBackList" value="ognl:item">
<tr>
<td colspan="4"><hr color=#000000></td>
</tr>
<tr>
<td width="25%"align="left"valign="top"><img
src="images/messageBoard/b1.gif"width="15"align="absmiddle"
height="15">
<span jwcid="@Insert"
value="ognl:ername"/><span jwcid="@Conditional"
condition='ognl:item.sex.equals("1")'>先生</span><span
jwcid="@Conditional"condition='ognl:item.sex.equals("0")'>女士</span>回复:
</td>
<td colspan="2"align="left"><span jwcid="@Insert" value="ognl:@com.cdmcs.util.FormatString@formatNewline(item.content)" raw="true">内容文字 </span></td>
</tr>
<tr align="right">
<td colspan="2"align="left">联系电话:<span
jwcid="@Insert"value="ognl:item.telephone"/> </td>
<td width="45%"align="left">回复日期:<span
jwcid="@Insert"value="ognl:item.messageDate"/></td>
<td width="1%"align="left"> </td>
</tr>
</span>
</table></td>
<td> </td>
</tr>
</table></td>
</tr>
</table></td>
<td width="17"background="images/bl_7.gif"> </td>
</tr>
<tr>
<td height="17"background="images/bl_10.gif"> </td>
<td background="images/bl_11.gif"> </td>
<td background="images/bl_12.gif"> </td>
</tr>
</table>
<br>
</span>
A)Foreach组件:
首先,我们接触到的第一Tapestry组件是Foreach组件。
Foreach是一个循环组件,它遍历source 参数,并在表现其内容前更新value参数。
这是Tapestry组件参数的至关重要特性:将一个属性与一个组件参数绑定,组件不仅读取被绑定的属性,而且更新属性。
Foreach组件使用<span>标签,当其表现(render)时,并不直接生成任何HTML代码。
它仅仅是将其包含的HTML标签和包含的组件重复表现。
<span jwcid="@Foreach"source="ognl:messageList"value="ognl:item" index="ognl:foreachIndex">
这里source参数通过OGNL从MessageBoard.java里面的获取getMessageList()方法:/**获取留言列表*/
public List getMessageList() {
return getGuestBookService().findAllMagMessage(maxResult, pageNo, 1); }
这里返回的是一个List,source参数可以接收List类型或者Object[]类型。
findAllMagMessage(maxResult, pageNo, 1) 是我们底层业务的一个方法,传递参数,返回一个包含MagMessage对象的List。
我们是使用spring+Hibernate作为业务逻辑层的框架,所以MagMessage继承于Persistable对象。
value="ognl:item"通过OGNL表达式,获取item,而item的值就是Foreach在每次循环遍历List时,赋的值。
打个比方,Foreach就好象实现了如下代码:
Persistable item = new Persistable();
for(int i=0, i<list.length; i++) {
item = (MagMessage)list.get(i);
foreachIndex = i;
/**
实现HTML代码
*
*/
}
item要获得Foreach每次循环赋予的对象,我们需要对其进行声明,在MessageBoard.page文件中,我们可以看见:
<property-specification name="item"type="com.cdmcs.bo.Persistable"/>在这里,Tapestry就是利用了Java的反射机制使得Tapestry能够访问类的方法,属性和构造函数。
Tapestry自己构造一个MessageBoard.java的子类,写入item的JavaBean方法。
所以,不要觉得奇怪,这就是为什么我们的java文件里面并没有getItem()和setItem()方法,仍然可以通过OGNL表达式赋值给item或者获取item的值。
index="ognl:foreachIndex"的含义是Foreach循环是,取出循环索引。
我们来看看这段代码:
/**获取回复列表*/
public List getWriteBackList() {
MagMessage mm =
(getMessageList().get(this.getForeachIndex()));
(MagMessage)
int i = mm.getId().intValue();
List list = getGuestBookService().findAllMagMessageByReplyId(i, 2);
return list;
}
是的,通过每次Foreach循环的索引,我们就可以直接得到Foreach循环的对象。
source参数的值是一个list,有了索引,我们微可以通过list.get(索引)来得到每次循环遍历的对象。
在这里,我们需要取出Foreach每次循环的对象,从该对象中取出id,然后再通过id去查询对应
该留言的所有回复。
这个getWriteBackList()返回的list,我们使用在外部Foreach嵌套的内部Foreach中:
<span jwcid="@Foreach"source="ognl:writeBackList"value="ognl:item">
<tr>
<td colspan="4"><hr color=#000000></td>
</tr>
<tr>
<td width="25%"align="left"valign="top"><img
src="images/messageBoard/b1.gif"width="15"align="absmiddle"
height="15">
<span jwcid="@Insert"
value="ognl:ername"/><span jwcid="@Conditional"
condition='ognl:item.sex.equals("1")'>先生</span><span
jwcid="@Conditional"condition='ognl:item.sex.equals("0")'>女士</span>回复:
</td>
<td colspan="2"align="left"><span jwcid="@Insert" value="ognl:@com.cdmcs.util.FormatString@formatNewline(item.content)"
raw="true">内容文字 </span></td>
</tr>
<tr align="right">
<td colspan="2"align="left">联系电话:<span
jwcid="@Insert"value="ognl:item.telephone"/> </td>
<td width="45%"align="left">回复日期:<span
jwcid="@Insert"value="ognl:item.messageDate"/></td>
<td width="1%"align="left"> </td>
</tr>
</span>
Foreach组件还有一个参数class="ognl:beans.evenOdd.next",在这里我们并没有使用。
含义是:在对应的page文件找寻类似:
<bean name="evenOdd"class="org.apache.tapestry.bean.EvenOdd"/>
的定义。
以便使用CSS来修饰Foreach循环(在style.css文件中):tr.even {
background-color: #ffffff;
}
tr.odd {
background-color: #eeeeee;
}
B)Insert组件:
我们可以在HTML模板中看见大量的Insert组件。
这个组件很简单,就是实现out.print()。