DisplayTag详解

【简介Introduction】DisplayTag是一个开源的自定义标签库(Custom Tag lib),他提供了直接而有效的格式化web视图层数据的有效手段。你可以在现在流行的web应用的MVC模式中集成DisplayTag到View层,其提供的强大表格格式化功能一定会令你爱不释手。或许上面说的有些夸张了,但是DisplayTag在表格的格式化方面表现确实出色,当然,他也只能显示表格,视图层的大部分工作不就是使用表格来格式化数据嘛?! 好了,让我们通过图片来看看他是一个什么样子吧!^_^怎么样?是不是感觉不错那?如果答案是肯定的,那么你一定急着想自己试一试咯?!不要急,下面就让我们开始我们的DisplayTag之旅。
[b]【Hello DisplayTag】[/b]既然是一个tutorial,所以,我们不想对像自定义标签的实现原理等进行解释,也就是说在此之前,我们假定你已经对自定义标签有一定的认识,当然,没有也无所谓,等这篇tutorial完成后,你估计就会了解的差不多了。 呐,让我们从最简单的displaytag的使用开始,就跟你的第一个程序往往是从HelloWorld程序开始一样。 先忽略其他的配置问题,我们的JSP文件的源代码如下:实际上,除去初始化和数据准备等操作,生成表格的代码只有一行,那就是:
[b]【配置configuration】[/b]Ok,在我们运用DisplayTag之前,我们需要对他的使用环境进行一些配置,或许有些复杂,但是,如果你是一个WebApp老手的话,其实并不难。 当然,在此之前,我们需要下载DisplayTag,当前的最新版本是displaytag-1.0-b3。你可以去SourceForge下载它,下载网址是:http://displaytag.sourceforge.net/download.html 。 2.1 DisplayTag的类库,依赖库和TLD文件的添加 解压下载下来的displaytag的压缩包,之后依次拷贝displaytag-1.0-b3.jar和lib目录下面的所有jar文件到你自己的WEBAPP_HOME/WEB-INF/lib目录下面,拷贝displaytag-11.tld,displaytag-12.tld和displaytag-el-12.tld到WEBAPP_HOME/WEB-INF目录下面。 他的依赖库包括:commons-beanutils,commons-collections,commons-lang以及commons-logging。 这里需要注意的问题就是,如果你连同Struts一起使用的话,DisplayTag的依赖库实际上都包括在Struts1.1的发布包中,你只需要将displaytag-1.0-b3.jar文件拷贝到你自己的WEBAPP_HOME/WEB-INF/lib目录下面就可以了。 另外一个重要的问题就是,如果你的Struts1.1发布包中的commons lang包不是2.0版本或者更高版本的话,需要去Apache的Jakarta commons项目主页上下载2.0版本的commons-lang类库,并替换掉原来的commons-lang类库,否则,运行的时候将报错误并不能运行。 2.2 web.xml的配置 要使用DisplayTag提供的自定义标签,跟其他自定义标签的使用没有什么两样,同样,需要在web.xml文件中注册taglib,下面是笔者的web.xml文件中taglib注册的片断:
[b]DisplayTag Tutorial by DarrenWang[/b] CopyRight June,2004:em510: By DarrenWang,All Rights Reserved!接上回书说道,:em325:[b]【displaytag提供的自定义标签说明】[/b]
DisplayTag一共提供了五种标签用来显示显示表格,他们是
Name属性:必须指定,表示scope中的数据标志,通过name来引用scope中的数据并进行显示。可以指定pageScope,requestScope,sessionScope和applicationScope。其中requestScope是缺省的scope,如果数据像request.setAttribute(“Infolist”,list)的形式放入requestScope,那么name属性可以直接像[name=“Infolist”]的形式指定。而像sessionScope的话,就要像上面的例子中那种形式指定了。
Id属性:指定显示表格的唯一标志,在后面你可以通过yourID_rowNum的形式显示或者使用每行数据的行号。比如:如果指定id=”tableID”,那么,<%=tableID_rowNum%>就会输出数据的行号。而指定id的另一个作用就是,如果一个页面中有多个分页显示表格的话,指定id后,各个表格的分页就可以工作正常。
Export属性:需要指定boolean型的值,如果指定export=true的话,表格显示完成后,下面会有一个输出项条目,指定数据导出的选项;否则,不显示数据导出条目。默认为false。 Pagesize属性:指定每页最多显示的数据总数。如果要显示的数据记录很长的话,指定pagesize后,数据将按照pagesize属性指定的数目显示记录数,其他的数据将分多个页面显示。如果不指定该属性,所有数据将在一个页面显示。 Class属性:指定表格显示所要使用的css风格,displaytag提供了ISIS,ITS,Mars,Simple,Report五种风格,默认是ISIS,也就是上面的黄色色调的风格。这些风格都是在screen.css文件中定义的,可以根据需要修改或者添加需要的风格。 RequestURI属性:当表格需要数据导出,排序或者分页显示的时候,因为要提交给指定的URL处理,而这个属性就是做这个事情的。
Sort属性:用来指定对数据进行排序的时候是对整个的数据list进行排序还是只对当前页面的数据进行排序。默认的不指定该属性的情况下,排序的时候只对当前页面数据进行排序;如果指定sort=“list”的话,则可以对整个的数据list进行排序。 OK,其他属性读者有兴趣或者需要的话,可以参考DiplayTag网站提供的manual。 3-2
Property属性:指定与该列显示数据相关联的property名称,该属性对应该行数据bean的属性,如果这列要显示bean的数据,column的这个属性是必须指定的。 如:
3-3
[b]【displaytag高级特性】[/b]有些时候,Collection中提供的数据或许不是我们想要的形式,比如,货币字段,在DataObject中或许只是存为int或者long甚至BigDecimal等形式,但是,显示的时候,我们不想以这种形式显示在页面上,这个时候,我们就需要借助DisplayTag提供的Decorator特性。Decorator可以帮助我们在显示数据之前对相应的数据进行格式化,然后再返回格式化后的结果进行显示。下面我们就DisplayTag提供的column decorator和table decorator 进行简单的剖析并列举其使用场合。4-1 colunm decorator阐述 Column Decorator所能实现的格式化功能只能针对单一的列进行,他所适用的情况包括对货币,时间,数字等类型进行统一格式化的情况,这样,就可以在应用中的所有表格中都能够使用该decorator进行格式化,大大提高了其可服用度。缺点嘛,从笔者个人的使用情况来看,column decorator不能对处于同一行的其他列的数据进行引用,对某些情况下的格式化很不方便,但这不属于Displaytag的问题,这些可以通过稍后将提到的Table decorator来实现。 要使用column decorator,需要实现DisplayTag库中的ColumnDecorator接口,这个接口位于org.displaytag.decorator.ColumnDecorator。同时,实现静态的decorate()方法。在这个方法中对要格式化列的数据进行格式化操作。或许这么说有些抽象,让我们来看一个例子。 这个实例其实是DisplayTag自带的,我在这里只是进行简单的讲解。 代码如下: import java.util.Date;import org.apache.commons.lang.time.FastDateFormat;import org.displaytag.decorator.ColumnDecorator;
public class LongDateWrapper implements ColumnDecorator{
private FastDateFormat dateFormat;
public LongDateWrapper() { dateFormat = FastDateFormat.getInstance("MM/dd/yyyy HH:mm:ss"); }
public final String decorate(Object columnValue) { Date date = (Date)columnValue; return dateFormat.format(date); }}该实例要对当前列的Date对象进行某种形式的格式化操作(实际上是MM/dd/yyyy HH:mm:ss的形式,这可以在code中看到),所以他在格式化方法decorate中提取该列的Date对象,并进行cast,然后使用commons-lang包中的FastDateFormat类对该对象进行格式化后返回String形式的结果。 与上原理所述,只要实现一个implemets了ColumnDecorator的类,并override自己相应的decorate()方法就可以了。另外,为了提高性能,最好是将初始化的操作放到该类的构造函数中,否则,当iterate数据记录的时候还要初始化资源,那性能可想而知不会高到那里去的。 4-2 table decorator阐述 Table Decorator笔者使用的更多一些,他可以对整个表格的输出在显示之前进行格式化。表格在显示的时候,每次iterate到一行记录的时候,都会首先查询decorator中是否实现了对各个列的数据对象进行格式化的方法,如果有,则调用这些方法对当前数据对象进行格式化,然后返回格式化后的结果进行显示;否则,直接返回当前数据对象进行显示。 如果说原始的数据对象不能够满足你数据显示需要的话,table Decorator就可以帮你忙。上面已经谈到过column Decorator遇到的问题:不能引用同行的数据对象,而现在table decorator就可以,你可以结合同一行的数据对当前行进行格式化,这在比如设置连接的时候要为连接设置不同的参数情况下特别有用。稍后实例将会有说阐明。 要编写Table decorator,首先需要继承DisplayTag包中的TableDecorator类,该类的确切位置是:org.displaytag.decorator.TableDecorator。之后,因为我们要可以对没一列都能进行格式化,所以,针对每一个要格式化的字段,只要想javabean的属性getter方法那样,实现每个字段的getter方法,并在该方法中实现针对该字段的格式化逻辑。 实例代码: import org.displaytag.decorator.TableDecorator;import org.apache.commons.beanutils.*;
public class UserTableDecorator extends TableDecorator{ public UserTableDecorator() { } public String getUserId() { Object obj = this.getCurrentRowObject(); DynaBean row = (DynaBean)obj; String deco = ""+row.get("userId"); return deco; }} 这是笔者后面实例中将会用到的一个TableDecorator,他是用来实现表格显示的时候能够生成便于用户选择的checkbox,以便用户可以选择一条或者多条数据进行删除或者其他操作。他生成的界面类似于:在这里,因为ValueObject的UserID字段只是返回一个String型的数据,这不能满足我们要显示checkbox的需要,所以,按照Javabean的getter方法形式,我们实现了public String getUserId()方法,并在这个方法中实现了将数据格式化为checkbox相关形式的操作。其中,只要取得了当前行的数据对象并cast成正确的对象类型,就可以调用该对象的方法使用同一行的其他列数据了。 4-3 其他,像表格的嵌套和表格的排序,表格的总结行的添加等功能,希望读者能够自己研读DisplayTag Samples的代码。 至此,有关Displaytag的decorator特性就算说完了。到现在为止,我们只是说了一些原理和简单的代码实例,为了给大家一个应用的意识,下面笔者就自己实现的一个简单工程做一个简单的描述,以期给大家一个更深的认识。------------------------------------------------------------------------
[b]DisplayTag Tutorial by DarrenWang[/b] CopyRight June,2004 By DarrenWang,All Rights Reserved!------------------------------------------------------话接上回:
[b]【table运行实例】[/b]
笔者原来做过几个有关Displaytag的demo,但是都只是研习用的,为了给出一个实际情况下的例子,笔者就前阵子的一个面试题给出一个用户管理流程的实现。当时要求用Hibernate跟persistence层交互,因为这跟我们的主题没有多大关系,所以,我们采用将数据以DynaBean的形式放入session来模拟数据的处理。5-1运行环境说明 IDE:Jbuilder X AppServer:Tomcat 4.0.6 Framework:Struts1.1(with Tiles and Validator) Other: DisplayTag(这个当然要有了,^_^),commons-beanutils,commons-lang2 另外在进行实例的进一步剖析之前,建议使用以下的目录结构:按照配置部分所述,建议将压缩包中的displaytag.war中的css目录和img目录都copy到当前的web应用根目录下面。另外,为所有的JavaScript文件建立单独的目录js(这里存放dTree的js类库),以便统一管理。因为我们使用到了Tiles,所以,拷贝整个Struts的Tiles应用中的layouts目录到当前应用目录。剩下的就是为工程用到的图片新建project-image目录,该名称可以变化,不要与img重复就可以。 另外一个目录事init目录,笔者用来存放init.jsp文件以及其他初始化作用的文件,init.jsp文件的内容将在后面提到。 最后,为每一个应用模块建立他们自己的目录,如memo和users,这些目录下面再根据需要细分其他子目录。 Ok,环境就说到这里。 5-2实例讲解 该实例其实很简单,说白了也就是一个CRUD操作,就是管理员可以实现对用户的创建,读取,更新和删除等功能。不过,MIS要处理的不就是这这些嘛,呵呵,ok,let’s begin。 因为什么都说的话太过繁琐,所以,笔者仅就整个流程和个别需要说明的内容做简单的描述,如果有兴趣读代码的话,可以email索取。 在管理员进入管理页面之前,我们需要一个Action来读取用户列表,以便管理员处理,所以,笔者定义了PreUserListAction,在这里,笔者构造了两个DynaBean并存入样例数据,然后转入管理员页面。 PreUserListAction的execute()方法的代码片断: HttpSession session = httpServletRequest.getSession(); List userList = new ArrayList(); DynaProperty[] props = new DynaProperty[]{new DynaProperty("userId",String.class),new DynaProperty("type",String.class),new DynaProperty("userName",String.class),new DynaProperty("passWord",String.class)}; BasicDynaClass userClass = new BasicDynaClass("user",null,props); try{ DynaBean user1 = userClass.newInstance(); user1.set("userId","00001"); user1.set("type","admin"); user1.set("userName","Darren"); user1.set("passWord","112345"); userList.add(user1);
DynaBean user2 = userClass.newInstance(); user2.set("userId","00050"); user2.set("type","plain"); user2.set("userName","susan"); user2.set("passWord","2125465"); userList.add(user2);
}catch(Exception e) { e.printStackTrace(); } session.setAttribute("UserList",userList); return actionMapping.findForward("suc"); 在转入用户管理页面后,管理员可以看到类似于下面的管理界面:这个页面就是使用DisplayTag生成的表格,至于其生成,上面说的已经足够了,唯一需要说明的一点就是最后一列,他同样是通过
除了使用DisplayTag外,该应用也使用了Tiles对这个应用的界面风格进行了统一,并且使用validator框架进行验证等,但这些不是这里的重点,所以不作赘述。
[b]【可能遇到的问题】[/b]
这是笔者在使用displaytag的时候遇到的或者是想到的一些问题,作为tips,列于下,以供参考。 6-1 进行分页显示的时候,虽然第一页可以正常显示,而且分页条目也显示出来,但是,点击连接的时候不能显示下其他数据,可能显示找不到该页面等错误。 这种情况多是因为没有在意数据存放的scope造成的,也是笔者刚开始使用的时候碰到的第一个比较伤脑筋的问题,其实当时只是为了试验,图方便而且也没过多在意,所有的数据都放在requestScope中了,而实际上,像分页这种情况,最合适的是应该将数据放入sessionScope中。否则,就会出现上面的情况。 解决方法:将数据collection放入sessionScope中,指定
[b]【结束语】[/b]
这篇文章定位为一篇tutorial,意在于与大家共享。没有多少高深的理论,笔者也尽量将问题说的简单,尽量的step by step。如果大家通过Google或者其他途径了解并阅读了他,并且觉得好的话,希望给一些鼓励的话,或者发张电子贺卡之类也可以,以资鼓励。 另外,我要感谢我现在的公司-江苏国光信息产业股份有限公司,虽然没有多少项目给我做,但是却给了我更多的时间,让我能够写这么些技术文章,与大家共享。
参考文献:1. DisplayTag官方网站提供有各种相关的文档,http://displaytag.sourceforge.net2. DisplayTag的FAQ或许有你所需要的答案,http://displaytag.sourceforge.net/faq.html