Tapestry学习九:关于组件(四)Loop, Output and DirectLink

 

基本上,这个页面就是在一个 table 中显示了所有的名人,并且 lastname 是个超链接,用户点击这个连接就会到所选中的名字的详细信息页面(这个页面以后再加)。

看看代码:

 <html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> 

<head> 

<title>Celebrity Collector: All Celebrities</title> 

</head> 

<body> 

<h1>All Celebrities in Collection</h1>

<table width="100%"> 

<tr t:type="loop" t:source="allCelebrities" 

t:value="celebrity"> 

<td> 

<a href="#" t:type="ActionLink" 

t:context="celebrity.id" 

t:id="detailsLink">${celebrity.lastName}</a> 

</td> 

<td>${celebrity.firstName}</td> 

<td> 

<t:output t:format="dateFormat" 

t:value="celebrity.dateOfBirth"/> 

</td> 

<td>${celebrity.occupation}</td> 

</tr> 

</table> 

<br/> 

<a href="#" t:type="PageLink" t:page="Start"> 

Back to the Start Page</a> 

</body>

</html>
  
可以看到在这个模板中有一个 HTML table ,这个 table 只有一行,但是这一行的三个属性,使我们隐藏了一个 Tapestry 的组件:

<tr t:type="loop" t:source="allCelebrities"  t:value="celebrity">

</tr>

t:type="loop"告诉我们这是一个Loop组件,它要干三件事:

1、它通过source属性来接受许多的对象。在我们的例子里是t:source="allCelebrities"属性,Tapestry将会在类文件中寻找getAllCelebrities方法,一般返回的是个集合。

2、它去通过这个集合反复的渲染Loop之间的内容,在这里就是一个<tr>

3、每一次遍历这个集合,Loop利用value提供的参数通过这个页面的类去创建一个类。在我们的例子中我们用t:value="celebrity"属性,Tapestry就会到页面类中去调用setCelebrity,然后把当前遍历到的类用参数传给利用Loop传进去。然后页面利用这个属性在页面中取值。

 关于那些表达式没什么好说的。${celebrity.occupation}

 看看这个新组件:

 <t:output t:format="dateFormat" t:value="celebrity.dateOfBirth"/>

 我们也可以用${celebrity. dateOfBirth }来代替这个组件,但是这样就会显示默认的格式,也许不是日期的格式,我们无法控制它的显示格式。OutPut提供给我们一个很好的机会我们可以用任何我们想用的形式去格式化将要显示的信息。将要显示的信息由value参数提供,显示的格式由format参数提供,你或许已经想到在接下来我们要做的事情之一就是要在ShowAll.java中创建一个getDateFormat的方法。

  最后看看我们在HTML的超链接中隐藏了一个ActionLink的组件,

<a href="#" t:type="ActionLink" t:context="celebrity.id"

t:id="detailsLink">

${celebrity.lastName}

</a>

ACtionLick可以作为一个基本的连接,结合Action使用,换句话说,是相当与处理事件的方式。PageLink的主要目标是跳转到另一个页面;而ActionLink的主要目的是通过用户点击这个链接去运行一段处理事件的代码,我们可以跳转到另一个页面,但是通常情况我们没有必要这样做。

ACtionLick在以前的版本中就是DirectLink,在一些显示实体的table中,这几乎成了很自然的选择,通过点击这个链接去显示一个条目的详细信息。ActionLinkt:id属性可以让我们和事件处理关联,大多数情况下它还有个t:context属性,可以提供一个参数给事件处理的方法,这我们的例子中是个名人的ID

 ShowAll.java的所有代码:

package com.packtpub.celebrities.pages;

import com.packtpub.celebrities.data.MockDataSource;

import com.packtpub.celebrities.model.Celebrity;

import com.packtpub.celebrities.util.Formats;

import com.packtpub.celebrities.model.User;

import java.text.Format;

import java.util.List;

import org.apache.tapestry.annotations.ApplicationState;

import org.apache.tapestry.annotations.InjectPage;

import org.apache.tapestry.annotations.OnEvent;

public class ShowAll 

{ 

@ApplicationState 

private User user; 

private boolean userExists; 

@ApplicationState 

private MockDataSource dataSource; 

@InjectPage 

private Details detailsPage; 

private Celebrity celebrity; 

String onActivate() 

{ 

if (!userExists) return "Start"; 

return null; 

} 

@OnEvent(component="detailsLink") 

Object onShowDetails(long id)

{ 

Celebrity celebrity = dataSource.getCelebrityById(id); 

detailsPage.setCelebrity(celebrity); 

return detailsPage; 

} 

public List<Celebrity> getAllCelebrities() 

{ 

return dataSource.getAllCelebrities(); 

} 

public Celebrity getCelebrity() 

{ 

return celebrity; 

} 

public void setCelebrity(Celebrity celebrity) 

{ 

this.celebrity = celebrity; 

} 

public Format getDateFormat() 

{ 

return Formats.getDateFormat(); 

}

}
  
这些都没什么好说的了。主要看看 MockDataSource ,这里使用这个具体的实现,而没有用 IDataSource 接口,这是因为在 Tapestry 中使用接口作为一个 ASO 还需要额外的一些配置。在以后我们会看到这个配置。
     其实这里将 MockDataSource 作为一个 ASO 是不合适的, ASO 只是一个方便的保存信息的对方,如果将数据源作为一个 Tapestry 的服务将会更好一些。但是路要一步一步的走,我们会渐渐的引入一些新的思路。定制服务将放在最后一张,现在关于 MockDataSource 这虽然不是个好的设计,但是至少能正常的工作。
  接下来我们还需要一个详细的页面。
  ActionLink 被渲染的时候,页面会产生一个 HTML 链接,并且凡是我们在 context 中提供的值都会在这个链接中被记录。也就是说, context 提供的参数会显示在 状态栏 上。
比如你选中第五个名人链接 在状态栏上会显示:
上面的数字 4 就是第五个名人的 ID
 
现在来看 detail 页面:
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">

    <head>

        <title>Celebrity Collector: ${celebrity.firstName} ${celebrity.lastName}</title> 

    </head>

    <body>

        <h1>Celebrity Details</h1>

        <table>

			<tr>

				<td>First Name:</td>

				<td>${celebrity.firstName}</td>

			</tr>

			<tr>

				<td>Last Name:</td>

				<td>${celebrity.lastName}</td>

			</tr>

			<tr>

				<td>Date of Birth:</td>

				<td><t:output t:value="celebrity.dateOfBirth" t:format="dateFormat"/></td>

			</tr>

			<tr>

				<td>Occupation:</td>

				<td>${celebrity.occupation}</td>

			</tr>

			<a href="#" t:type="PageLink" t:page="ShowAll">Back to All Celebrities</a>

		</table>

        



    </body>

</html>
类:
 
package com.packtpub.celebrities.pages;

import com.packtpub.celebrities.model.Celebrity;

import org.apache.tapestry.annotations.Persist;

import java.text.Format;

public class Details 

{ 

@Persist 

private Celebrity celebrity; 

public void setCelebrity(Celebrity c) 

{ 

this.celebrity = c; 

} 

public Celebrity getCelebrity() 

{ 

return celebrity; 

} 

public Format getDateFormat() 

{ 

return Formats.getDateFormat(); 

}

}

  
现在运行这个应用,你发现一切都是正常的。

现在我觉得这个名人给我留下的印象太深刻了,我想把这个保存为书签,以后随时可以打开浏览。试一下~你会发现书签不能用,原因是个空指针错误,原因是pageclasscelebrity属性为空。为什么呢?原因大家都知道,persist是把这个属性放到了session中,当你以后通过书签打开的时候,这个session早就失效了。

ActionLink可以在生成的链接中记录你选择的名人的ID,但是这个ID在查找完名人后没有被保存下来。只是查到的名人类被保存在session中,当session失效时,自然就找不到了。

当用户存为书签登以后再看的时候,页面总是给他一个异常,这是很不合适的。为了防止这一点,我们可以使用anActivate方法来检查celebrity是否为空,如果是这样,我们给它重定向到其他的页面,但这不是一个优雅的解决方法,也是不友好的。有一种方法既显示详细信息页,并使它能保存书签。你已经猜到,我们可以使用page activation context

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值