:Grid 组件
以前, 我们用LOOP组件去展示集合中的名人.它也并不困难,而且在许多情况下,他完全可以解决你的要求。 但是, 随着集合中名人的增多,问题也就随之而来.
我们不想一下子把所有的名人都显示在一个页面上,我们需要一个分页的机制,同时可对姓名和职业等进行排序。我们可以增加很多代码和控制去实现我们想要的效果,但表的分页和排序的列是用户界面的非常整体的一部分,并且每一次展现它都不是很有效率。
还好有Grid组件,把showall.tml中的关于loop的代码:
<table width="100%">
<tr t:type="loop" t:source="allCelebrities"
t:value="celebrity">
<td>
<a href="#" t:type="PageLink" t:page="Details"
t:context="celebrity.id">
${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>
替换成:
<t:grid t:source="allCelebrities"/>
运行,到showall的页面去看一看:
只用一行代码就展现除了另我们印象深刻的效果是不是(说实话,代码是很少,但是效果不咋地,可以说很丑,不及EXT一半的强大)?不仅我们所有的名人都显示在一个整齐的表中,而且点击表头的列还可以排序。注意看职业列,因为enum的原因在前面显示的全是大写的字母那个现象不见了,现在是首字母大写的。
在这里我们看到了Tapestry强大的另一面,Grid组件唯一需要的参数就是source,这个参数和Loop组件的source参数一样,通过这个参数,Grid组件收到很多同一类型的对象,它取出集合中的第一个对象并查找它的属性,grid试着为每一个属性都创建个列,把属性的名字变列名,并做出一些额外的调整,比如在我们列子中的职业列。
一切都很让你兴奋,但是这个table还有很多不足:
1、 所有的名人都在这个页面上,并没有出现我们想要的分页,这是因为,默认的分页条目是25条,多于我们集合中的名人数。
2、 在lasename列上没有超链接去查看详细信息。
3、 ID属性没有必要显示出来。
4、 列的排序是不合理的,lastname应该在firstname之前,然后是date of brith。
注:Grid默认的列的排序是根据展示的实体类当中的getter方法的顺序去排序的。
还有其他的问题我们要去当心的,但是现在我们先解决这是个问题。
打造Gird
首先我们来改变每页显示的记录,在要在grid组件中加个属性就可以了:
<t:grid t:source="allCelebrities" rowsPerPage=” 5” />
效果:
说一下:作者用的版本默认分页的链接在下面,最近版本的默认分页链接在上面。Tapestry这狗日的就这么善变。这时需要加个pagerposition属性:"top", "bottom", "both" or "none".知道是什么意思了吧。等会儿可能还会说要更个性化的。
下面我们将要为lastname列增加超链接,去显示详细页面了,这时就必要要知道每一行是那一个名人,这就需要grid增加一个属性:row去保存每一行的实体。
<t:grid t:source="allCelebrities" rowsPerPage="5" row="celebrity"/>
当然在页面类中要有celebrity属性,我们就用Loop留下的那个。
接下来我们就要告诉tapestry的grid在渲染到lastname这一列的时候,我们不希望它按照默认的方式去显示,而要以我们的方式去显示
<t:grid t:source="allCelebrities" rowsPerPage="5" row="celebrity">
<t:parameter name="lastNameCell">
<t:pagelink t:page="details" t:context="celebrity.id">
${celebrity.lastName}
</t:pagelink>
</t:parameter>
</t:grid>
在这里,gird组件包含了一个特殊的Tapestry元素:<t:parameter/>,是不是和我们以前说IF组件是包含在IF组件中的那个提供另一种显示的内容的东西很象?在这里,它包含的内容将填补在last name这一列。Tapestry是怎么知道的?根据元素的名字:lastNameCell,这个名字的第一部分:lastName,就是要展示的类中的一个属性,最后一部分:Cell,告诉tapestry这个表格的单元的内容将显示我们指定的内容。
运行看看:
现在就剩下去掉我们不想要的列,和重新对表头进行排序了。我们将用到两个属性:Grid-remove和reorder。修改模板中的组件定义:
<t:grid t:source="celebritySource" rowsPerPage="5"
row="celebrity"
remove="id"
reorder="lastName,firstName,occupation,dateOfBirth">
<t:parameter name="lastNameCell">
<t:pagelink t:page="details" t:context="celebrity.id">
${celebrity.lastName}
</t:pagelink>
</t:parameter>
</t:grid>
现在再运行一下看看:
注:reorder不能删除列,如果你在reorder中忽略了一个列,那么这个列将现在是表格的最后。
十五:改变列的标题
列的标题是由Tapestry自动产生的,要想改变标题,最有效率的方法是利用资源文件。就象我们在说select组件用到的。在app.properties中加上这么一行:
dateOfBirth-label=Birth Date
运行该程序,你会发现生日的标题改成了Birth Date,在这里通过附加”-label”所定义的值显示在了标题中。
现在对于大部分的要求你都可以调整grid组件去解决,并且在它的帮助下可以显示不同的对象。然而,还有一个问题:
在SHOWALL.JAVA中的getAllCelebrities方法中加个输出语句:
public List<Celebrity> getAllCelebrities()
{
System.out.println("Getting all celebrities...");
return dataSource.getAllCelebrities();
}
目的是为了在调用这个方法的时候让我们知道。每当表格展示名人的时候,都会输出这么一句。
Gird组件有个source属性赋给一个allCelebrities值,所以它调用getAllCelebrities去获取显示的数据。注意,grid在这个方法后调用,收到了15条数据,但是它值显示5条。
点击第二页,我们看到gird又调用了getAllCelebrities方法,并且收到了所有的数据,还是只显示5条,同样的第三页,第四页也是如此。这是非常浪费资源的方式。
想象一下如果我没有10000条数据,并且这些数据都在一个远程的数据库上,那么这个请求会浪费我们极大的资源,特别是有2000页。
我们需要一种更有效率的方式去访问需要的名人们,一页又一页的,在第一页的时候取出要在第一页显示的数据,在第二页的时候就取出本该在第二页显示的数据……Tapestry为我们提供了这种方式,我们只要提供一个GridDataSource接口的实现。