-
Servlet 2.3或更高版本
-
JDK 1.3.1或更高版本
最小的Jars要求:
-
commons-beanutils 1.6
-
commons-collections 3.0
-
commons-lang 2.0
-
commons-logging 1.0.4
-
standard 1.0.2
PDF導出需要的Jars:
-
minimum jars (above)
-
avalon-framework 4.0
-
batik 1.5-fop-0.20-5
-
fop 0.20.5
-
xalan 2.5.1
-
xercesImpl 2.6.1
-
xml-apis 2.0.2
XLS導出需要的Jars:
-
minimum jars (above)
-
poi-2.5.1.jar
從sourceforge
下載發行包。(http://sourceforge.net/projects/extremecomp/)
在壓縮文件里你能找到開始使用需要的所有東西:
-
extremecomponents.jar
-
extremecomponents.tld
-
extremecomponents.css
-
默認的一組圖片
-
源代碼
-
test.jsp (用于確認安裝是否正確)
將extremecomponents.jar文件拷貝到你的工程的/WEB-INF/lib目錄下。
處理TLD文件有兩種方式。 你可以把extremecomponents.tld文件放到WEB-INF目錄下的任何地方。
不過,為了便于管理,我喜歡把我的TLD文件都放到/WEB-INF/tld目錄下。你需要根據你的extremecomponents.tld
文件的位置來修改/WEB-INF/web.xml文件的標簽映射。
<taglib>
<taglib-uri>/tld/extremecomponents</taglib-uri>
<taglib-location>/WEB-INF/tld/extremecomponents.tld</taglib-location>
</taglib>
隨后,你需要向下面一樣在你的JSP里把eXtremeTable包含進來:
<%@ taglib uri="/tld/extremecomponents" prefix="ec" %>
如果你的servlet容器支持JSP 1.2 (或更高版本),它將能夠自動發現TLD文件,那么你什么也不需要做。
當extremecomponents.jar被容器加載的時候,在它的META-INF目錄下的extremecomponents.tld文件將被找到。
這時,你需要向下面一樣在你的JSP里把eXtremeTable包含進來:
<%@ taglib uri="http://www.extremecomponents.org" prefix="ec" %>
為了使用eXtremeTable樣式,從styles目錄拷貝extremecomponents.css到你存放.css腳本的地方。
當然在JSP頁面里,你需要提供一個到CSS的鏈接。就像我將我的樣式表放在/styles目錄下。
<%@ taglib uri="/tld/c" prefix="c" %>
<link rel="stylesheet" type="text/css" href="<c:url value="/styles/extremecomponents.css"/>">
為了使導出功能有效,你需要設置導出過濾器。這是一個僅用于導出功能的可選配置。
如下所示在/WEB-INF/web.xml里配置過濾器:
<filter>
<filter-name>eXtremeExport</filter-name>
<filter-class>org.extremecomponents.table.filter.ExportFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>eXtremeExport</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
過濾器還有一個可選的初始化參數,用于決定什么時候生成報頭(headers)。我發現大多數的servlet容器
傾向于在調用過濾器的doFilter()方法后才設置響應報頭(response headers)。然而,一些servlet容器只有在
調用過濾器的doFilter()方法前設置響應報頭,過濾器才能正常工作。默認的方法是調用過濾器的doFilter()方法后
設置響應報頭,你可以通過使用responseHeadersSetBeforeDoFilter這個初始化參數調整它。
<filter>
<filter-name>eXtremeExport</filter-name>
<filter-class>org.extremecomponents.table.filter.ExportFilter</filter-class>
<init-param>
<param-name>responseHeadersSetBeforeDoFilter</param-name>
<param-value>true</param-value>
</init-param>
</filter>
如果你使用了Sitemesh,你將需要包含SitemeshPageFilter。SitemeshPageFilter擴展了正常的
sitemesh的PageFilter,它使得正在進行導出的JSP頁面不被修飾。
如下所示在/WEB-INF/web.xml里配置過濾器:
<filter>
<filter-name>Sitemesh</filter-name>
<filter-class>org.extremecomponents.table.filter.SitemeshPageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
使用發行包的test.jsp來測試安裝。將test.jsp拷貝到web應用的最頂層, 默認的圖片文件在
/images/table/子目錄下。為了測試,創建相應的目錄并將拷貝所有需要的圖片。所有工作都完成后,你可以在瀏覽 器里運行test.jsp了。
提示: 我不提倡在JSP里使用腳本(scriplets),但為了不用使用框架而能進行快速測試,在test.jsp
使用腳本是唯一的辦法。
TableTag用來設定什么被顯示并且如何進行顯示。默認的eXtremeTable在servlet范圍(按照page,request,
session,applicaton的順序)尋找具有名稱和items屬性設置相同的Beans集合(如前章所述它指Beans和Maps兩種集合)。
表將遍歷所有列,它使用var屬性將當前行對應的bean從集合傳到page范圍,因此你可以從page范圍中重新得到這些數據
進行操作。tableId用來唯一標識表,如果在JSP頁面里包含兩個或兩個以上的表時需要設置它。
President bean定義如下:
public class President implements Serializable {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Beans集合需要被組裝并傳到servlet范圍中。我喜歡使用Spring框架,因此示例將使用Spring框架的
控制器(Controller)。如果你正在使用Struts,它和Action的功能類似。如果你使用別的東西,比如直接使用
servlets,你只需要明白我所做的只是組裝Beans集合并傳到request范圍中。
public class Presidents extends AbstractController {
protected ModelAndView handleRequestInternal(HttpServletRequest request,
HttpServletResponse response)
throws Exception {
List presidents = new ArrayList();
President president = new President();
president.setFirstName("George");
president.setLastName("Washington");
presidents.add(president);
president = new President();
president.setFirstName("John");
president.setLastName("Adams");
presidents.add(president);
request.setAttribute("presidents", presidents);
return new ModelAndView("/demo/presidents.jsp");
}
現在你可以構造表了:
<%@ taglib uri="/tld/extremecomponents" prefix="ec" %>
<ec:table
items="presidents"
var="pres"
imagePath="${pageContext.request.contextPath}/images/*.gif"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:column property="firstName"/>
<ec:column property="lastName"/>
${pres.lastName}
</ec:column>
</ec:table>
從本示例中你應該知道我們將名為presidents的Beans集合以presidents為名稱放到request中。
為了使表知道如何找到這個Beans集合,我們設置TableTag的items屬性為presidents。同時我們定義
了兩列:firstName和lastName。firstName列是最普通的用法:我們僅僅想讓這列從當前bean中得到相應
firstName的值;lastName列示另外一種用法:明確取得值。
從一列中明確取得值非常有用,但是你需要理解表是如何構造行的。為了構造行,表需要對所有行進行
rowsDisplayed屬性設定次數的迭代。每次迭代都從Beans里取得下一個bean并使用var屬性設定的名稱傳入page
范圍。也可以說每次迭代你都訪問的是集合中當前行對應的bean。
為了顯示圖片需要設置imagePath屬性:
<ec:table
items="presidents"
var="pres"
imagePath="${pageContext.request.contextPath}/images/*.gif"
>
...
</ec:table>
eXtremeTable將找到一個目錄下的所有圖片并使用特殊的語法來定義他們是那類圖片。
本示例中所有的圖片都直接保存在web上下文的images目錄下。*.gif使eXtremeTable知道所
有的圖片都是GIF格式的。在我們討論preferences后,你將發現你可以你可以通過在
extremecomponents.properties文件中設定這個屬性,而不用再整個應用的每個eXtremeTable 中包含它。
eXtremeTable內嵌了過濾和排序功能,你只需要決定是否需要使用他們。你要使用的屬性是
filterable和sortable,他們都是布爾值并且默認值是true。默認的所有特性都有效,你可以按照
需要來關掉一些特性。比如,如果你不想使用排序或過濾你可以把他們的屬性設為false。
<ec:table
items="presidents"
var="pres"
imagePath="${pageContext.request.contextPath}/images/*.gif"
action="${pageContext.request.contextPath}/presidents.run"
filterable="false"
sortable="false"
>
...
</ec:table>
如果你仍不確信,你可以來驗證他們。首先,設置filterable和sortable為true,你將看到
eXtremeTable允許你輸入關鍵詞來過濾結果集,它也允許你通過在頁頭(header)上滾動鼠標來排序。
然后,設置filterable和sortable為fale,你將發現所有這些特性都不允許使用。
本示例需要指出的是使用action屬性,action被用來告訴eXtremeTable當過濾或排序時如何回
到當前的頁面。本例中我通過Spring框架的controller(在這里是presidents.run)來得到Beans集合。
你不需要擔心傳參問題,eXtremeTable將保存所有的參數并將它們和過濾器、排序、分頁一起傳遞給
Beans集合。更詳細的信息請參考ParameterTag。
默認地eXtremeTable一頁將顯示15行。你可以通過設定rowsDisplayed屬性為你想顯示行數的數
值來改變它。rowsDisplayed也可以在extremecomponents.properties文件中設定。(參考Preferences)。
提示:如果你想在一頁中顯示所有行,只需要設置showPagination為false。
TableTag關聯了很多樣式屬性:
<ec:table
cellspacing="0"
cellpadding="0"
border="0"
width="80%"
style=""
styleClass=""
/>
所有這些都是可選的。
表新增了兩個屬性:state和stateAttr。state屬性參照State接口并能插接如何保存表的狀態的不同實現。
State借口如下:
public interface State {
public void saveParameters(TableModel model, Map parameters);
public Map getParameters(TableModel model);
}
state屬性使用預設的四種狀態(default、notifyToDefault、persist和notifyToPersist)之一,
你也可以插接自己的實現。default狀態不維持任何狀態;persist狀態沒有任何參數傳入,將一直維持表的狀態;
notifyToDefault狀態將一直維持表的狀態直到你傳入參數告訴它回到default狀態;notifyToPersist狀態
將一直維持當前狀態直到你傳入參數告訴它維持persisted狀態。
stateAttr為指定參數提供了一條途徑,你也可以使用屬性文件在全局范圍內指定它。
為了向后兼容,默認參數一直為useSessionFilterSort。
如果你想state按照不同方式工作你只要實現State接口,然后使用TableTag的state屬性來指定實現類的 全路徑。
作為一條首要規則當使用state屬性時,需要指定tableId。這是因為state使用tableId為名保存在session里。
如果tableId不唯一,eXtremeTable將覆蓋另一個同名的內容。tableId默認值為ec。
為了保持一致性,所有的顯示特性都命名為showXXXX。他們包括showPagination、showStatusBar、
showTooltips、和showExports。
title屬性將在表的上方顯示標題,標題的位置根據使用的視圖不同而不同。當前默認視圖中標題位于表的上方 工具條的左邊。更詳細的信息請參考View。
你會發現還有一些屬性沒有被探討,因為他們將在其他章探討。autoIncludeParameters在ParameterTag里被探討;
retrieveRowsCallback,sortRowsCallback和filterRowsCallback在Callbacks里被探討。
大多數標簽包含一系列的固定屬性,這樣那些已經實現的功能能夠被使用。然而,eXtremeTable具有一種更具彈性的架構,
你可以添加自己的標簽屬性實現更多的定制工作。此外,eXtremeTable提供了非常清晰的鉤子(hooks)允許你得到那些定制的
標簽屬性來做一些你需要的工作。
通過addExtendedAttributes()方法將擴展屬性包含到eXtremeTable里:
public void addExtendedAttributes(Table table);
如果方法被覆蓋TableTag將調用它。你需要做的就是擴展TableTag,覆蓋addExtendedAttributes()方法,然后添加自己
的屬性到表對象中。一個定制的TreeTag示例如下:
public class TreeTag extends TableTag {
private String parentAttribute;
private String identifier;
public void setParentAttribute(String parentAttribute) {
this.parentAttribute = parentAttribute;
}
public void setIdentifier(String identifier) {
this.identifier = identifier;
}
public void addExtendedAttributes(Table table) {
table.addAttribute(TableConstants.PARENT_ATTRIBUTE, TagUtils.evaluateExpressionAsString("parentAttribute",
parentAttribute, this, pageContext));
table.addAttribute(TableConstants.IDENTIFIER, TagUtils.evaluateExpressionAsString("identifier",
identifier, this, pageContext));
table.setFilterRowsCallback("org.extremecomponents.tree.ProcessTreeRowsCallback");
table.setSortRowsCallback("org.extremecomponents.tree.ProcessTreeRowsCallback");
}
}
現在你添加了屬性值到table對象。
另外,你也可以定制自己的標簽和自己的TLD文件。你不需要修改extremecomponents.tld文件。
你能象使用eXtremeTable里的標簽一樣使用自己的標簽,除了使用你自己標簽的參照。假如你的標簽參照為mycompany
并且標簽為customTable,你可以像下面一樣使用他們:
<mycompany:customTable
items="presidents"
action="${pageContext.request.contextPath}/public/demo/presidents.jsp"
title="Presidents"
>
<ec:row>
<ec:column property="nickName"/>
</ec:row>
</mycompany:customTable>
ColumnTag用來定義表中的列。
示例President Bean:
public class President implements Serializable {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
下例生成firstName和lastName列:
<ec:table
items="presidents"
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
/>
<ec:row>
<ec:column property="firstName"/>
<ec:column property="lastName">
${pres.lastName}
</ec:column>
</ec:row>
</ec:table>
通過對TableTag的討論,已經知道列可以通過動態或精確的方式得到他們的值。
firstName列動態地取得相應的值,列找到當前的bean并調用相應的getFirstName()取得值。
lastName列明確地從當前bean取得值,它要求你自己取得相應的值。如下例:
<ec:table
items="presidents"
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="lastName">
${pageScope.pres.lastName}
</ec:column>
</ec:row>
</ec:table>
從page范圍中取得名為pres的bean并得到它對應的lastName屬性值。如果你正使用
Beans集合請確認具有對應的getter方法;如果使用Maps集合則不需要任何別的動作,
eXtremeTable能夠通過屬性名從Map中得到對應的值。
提供這種可選取值方法的主要原因是使你能夠對其他類型的html標簽提供動作支持,例如顯示 一幅圖片或者通過定義href使該列成為到其它頁的一個鏈接。
<ec:table
items="presidents"
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="lastName">
<a >${pageScope.pres.lastName}</a>
</ec:column>
</ec:row>
</ec:table>
切記bean中所有的屬性都是可訪問的,因此你甚至可以通過firstName屬性
來顯示下一頁。請注意firstName屬性是如何作為URL字符串傳輸的。
<ec:table
items="presidents"
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
/>
<ec:row>
<ec:column property="lastName">
<a >
${pageScope.presidents.lastName}
</a>
</ec:column>
</ec:row>
</ec:table>
我將不再在任何示例中強調pageScope。JSP標簽總是最先在pageScope中尋找任何對像,
因此我們總是能安全地返回正確的bean。
每一列總是被實現Cell接口的對象修飾,你可以認為Cell是一個為了html顯示或導出而返回格式化值的對象。
發行包包含的Cell有DisplayCell、DateCell、 NumberCell和RowCountCell。
DisplayCell是僅僅顯示列值的默認cell;DateCell使用parse屬性(可選)和format屬性來格式化對應的屬性值;
NumberCell使用format屬性來格式化對應的屬性值;RowCountCell顯示當前行。
提示:為了避免混亂并提高靈活性Cell接口已經被修改。而且對于區別
如何處理html和導出顯示值也不是很清晰。以前列值作為html顯示,列的propertyValue作為導出使用。
另外列值和propertyValue已經重寫,他們以前在view中是不能被訪問的。
cell現在是singleton并且不再線程安全,改變的原因是為了Cell接口能更簡單地被使用。
init()和destroy()方法作為singleton更靈活但是處于一種混亂的狀態。
Cell接口如下:
public interface Cell {
/**
* The display that will be used for the exports.
*/
public String getExportDisplay(TableModel model, Column column);
/**
* The html that will be displayed in the table.
*/
public String getHtmlDisplay(TableModel model, Column column);
}
現在得到導出和html顯示存在明顯的區別。更重要的,需要返回字符串。列值和屬性值不再 需要設置。
DisplayCell是擴展AbstractCell的最簡單的Cell。AbstractCell定義
的虛擬方法getCellValue用來返回cell的值。雖然AbstractCell在一些情況下是有用的, 但更多情況下只需要直接實現Cell接口。
DisplayCell:
public class DisplayCell extends AbstractCell {
public String getExportDisplay(TableModel model, Column column) {
return column.getPropertyValueAsString();
}
protected String getCellValue(TableModel model, Column column) {
return column.getValueAsString();
}
}
AbstractCell:
public abstract class AbstractCell implements Cell {
public String getExportDisplay(TableModel model, Column column) {
return getCellValue(model, column);
}
public String getHtmlDisplay(TableModel model, Column column) {
HtmlBuilder html = new HtmlBuilder();
CellBuilder.tdStart(html, column);
CellBuilder.tdBody(html, getCellValue(model, column));
CellBuilder.tdEnd(html);
return html.toString();
}
/**
* A convenience method to get the display value.
*/
protected abstract String getCellValue(TableModel model, Column column);
}
現在你應該知道Cell是多么簡單。只需通過實現Cell接口或擴展AbstractCell來定制你自己的Cell,
并設置列標簽的Cell屬性為類的全路徑。例如: 如果你定制了一個名為MyCell的Cell,那么你可以像下面一樣使用它:
<ec:column property="firstName" cell="com.mycompany.cell.MyCell"/>
如果你改變列的數據,那么過濾或排序可能沒有意義。切記我的意思是如果你人為地改變數據, 而不是使用樣式對它進行包裝或作為<a href>包含。
如果你的定制cell顯示數據的樹狀視圖,或者是一幅圖片, 那么過濾和排序等一切邏輯操作都是沒有意義的。
列的filterCell屬性控制過濾器如何顯示,它和cell屬性非常相像并且也是實現Cell接口。
已經定義了兩個過濾器cells:默認的和droplist。默認的是一個輸入框元素,除非你確信你需要使這列可以進行過濾, 否則你不需要做任何事。
你可以像下面一樣使用droplist過濾器Cell:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="status" filterCell="droplist"/>
</ec:row>
</ec:table>
filterCell也允許你定義定制的過濾器,所有你必須做的就是實現Cell接口或者擴展AbstractCell,
并設置列標簽的Cell屬性為類的全路徑。例如,如果你定制了一個名為MyCell的Cell,那么你可以像下面一樣使用它:
<ec:column property="firstName" filterCell="com.mycompany.cell.MyFilterCell"/>
參閱Cell節了解如何創建你自己定制Cells的更多信息。
headerCell屬性控制headers如何顯示,它和cell屬性非常相像并且也是實現Cell接口。 默認header
cell作為文本顯示,包含排序邏輯。
headerCell也允許你定義定制的過濾器,所有你必須做的就是實現Cell接口或者擴展AbstractCell,
并設置列標簽的Cell屬性為類的全路徑。例如,如果你定制了一個名為MyCell的Cell,那么你可以像下面一樣使用它:
<ec:column property="firstName" headerCell="com.mycompany.cell.MyHeaderCell"/>
參閱Cell節了解如何創建你自己定制Cells的更多信息。
ColumnTag關聯了很多樣式屬性:
<ec:column
width=""
style=""
styleClass=""
headerStyle=""
headerClass=""
filterStyle=""
filterClass=""
/>
所有這些都是可選的。style屬性定義列內聯的樣式;styleClass允許你定義一個列顯示的css類;
headerClass屬性允許你改變header列的css類;filterClass屬性允許你改變filter列的css類。
解析和格式化屬性被用在日期和貨幣的顯示上。
和date交互的工作依賴于你的bean屬性是否是一個字符串或者是一個Date對象。
如果是一個字符串那么你就需要確定parse屬性,parse屬性是按照模板定義來解析一個字符串為
一個日期對象。如果bean中的屬性是日期型對象則不需要添加parse屬性。不論如何你都需要設置format屬性。
format屬性按你提供的模板對值進行格式化。
本示例中使用MM/dd/yyyy模板格式化日期型值。因為bean中的born屬性值為字符串,所以我們需要
使用parse屬性來將它轉換成日期型數值。
<ec:column property="born" cell="date" parse="yyyy-MM-dd" format="MM/dd/yyyy"/>
對于貨幣只需要設置format屬性:
<ec:column property="payroll" cell="currency" format="###,###,##0.00"/>
很多時候在extremeTable中,你使用同樣的模版來解析和格式化日期和貨幣值。
所以便利的方法是在你自己的extremecomponents.properties文件中定義解析和格式化屬性。
參閱Preferences章了解更多信息。
你可能記得在TableTag中看見過filterable和sortable屬性,ColumnTag中也有相同的屬性。
列的filterable和sortable屬性將覆蓋表的filterable和sortable屬性設置。當你需要除了對表中的一、兩列之外的
所有列進行過濾和排序時,十分便利。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="firstName" filterable="false"/>
<ec:column property="lastName" sortable="false"/>
</ec:row>
</ec:table>
列新增了兩個屬性:calc和calcTitle:
<ec:column property="data" calc="total" calcTitle="Total:" />
calc屬性實現具有唯一方法的Calc接口:
public interface Calc {
public Number getCalcResult(TableModel model, Column column);
}
它傳入model和column,并返回一個Number型的值。默認的實現為總計和平均值。
為了使用定制的Calc,只需要使用ColumnTag的calc屬性來指定實現Calc接口的實現類的 全路徑。
Calc為singleton并且不是線程安全的,因此不要定義任何類變量。
viewsAllowed屬性制定類允許使用的視圖。視圖包括:html、pdf、xls、csv,以及任何定制的視圖。
如果你指定一個或幾個視圖,那么列僅能使用這些指定的視圖。例如:你指定viewsAllowed="pdf",這意味著
這列只允許PDF導出,而不能進行其他格式的導出或html視圖。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="firstName"/>
<ec:column property="lastName" viewsAllowed="pdf"/>
</ec:row>
</ec:table>
viewsDenied屬性制定類不允許使用的視圖。視圖包括:html、pdf、xls、csv,以及任何定制的視圖。
如果你指定一個或幾個視圖,那么列僅這些指定的視圖不能被使用。例如:你指定viewsDenied="html",這意味著
這列不允許使用html試圖,但能進行任何形式的導出。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
>
<ec:row>
<ec:column property="firstName"/>
<ec:column property="lastName" viewsDenied="html"/>
</ec:row>
</ec:table>
title屬性用來為header設定一個描述性的名稱。如果你不定義title那么列將使用屬性名。
如果你不想顯示任何title,你只需要設置title屬性值為一個空白(whitespace)。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
title="Presidents"
>
<ec:row>
<ec:column property="firstName"/> //title shows as First Name
<ec:column property="firstName" title="First Name"/> //title shows as First Name
<ec:column property="firstName" title=" "/> //no title shows up
</ec:row>
</ec:table>
大多數標簽包含一系列的固定屬性,這樣那些已經實現的功能能夠被使用。然而,eXtremeTable具有一種更具彈性的架構,
你可以添加自己的標簽屬性實現更多的定制工作。此外,eXtremeTable提供了非常清晰的鉤子(hooks)允許你得到那些定制的
標簽屬性來做一些你需要的工作。
通過addExtendedAttributes()方法將擴展屬性包含到eXtremeTable里:
public void addExtendedAttributes(Column column);
如果方法被覆蓋ColumnTag將調用它。你需要做的就是擴展ColumnTag,覆蓋addExtendedAttributes()方法,然后添加自己
的屬性到列對象中。一個定制的CustomTag示例如下:
public class MyCustomTag extends ColumnTag {
private String customAttributeOne;
public String getCustomAttributeOne() {
return customAttributeOne;
}
public void setCustomAttributeOne(String customAttributeOne) {
this.customAttributeOne = customAttributeOne;
}
public void addExtendedAttributes(Column column) {
column.addAttribute("customAttributeOne", customAttributeOne);
}
}
現在你添加了屬性值到Column對象,現在你可以像下例一樣來定制cell:
public class MyCustomCell implements Cell {
public String getHtmlDisplay(TableModel model, Column column) {
Object customAttributeOne = column.getAttribute("customAttributeOne")
String customAttributeOne = column.getAttributeAsString("customAttributeOne")
}
}
另外,你也可以定制自己的標簽和自己的TLD文件。你不需要修改extremecomponents.tld文件。
你能象使用eXtremeTable里的標簽一樣使用自己的標簽,除了使用你自己標簽的參照。假如你的標簽參照為mycompany
并且標簽為customColumn,你可以像下面一樣使用他們:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/public/demo/presidents.jsp"
title="Presidents"
>
<ec:row>
<mycompany:customColumn
property="hello"
cell="com.mycompany.cell.MyCustomCell"
customAttributeOne="Hello World"/>
</ec:row>
</ec:table>
eXtremeTable具有導出不同格式文件的功能,導出的數據為過濾和排序后的所有結果集,
分頁不會影響返回的結果集。換句話說,如果表數據分多頁顯示,那么所有頁的數據都將被導出。 導出的格式為Microsoft Excel (OpenOffice
Calc)、PDF和CSV。
使用ExportXlsTag導出Microsoft Excel (OpenOffice Calc):
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
/>
<ec:exportXls
fileName="presidents.xls"
tooltip="Export Excel"/>
...
</ec:table>
使用ExportPdfTag導出PDF。所有要做的就是指定fileName屬性和一些樣式屬性:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
/>
<ec:exportPdf
fileName="presidents.pdf"
tooltip="Export PDF"
headerColor="blue"
headerBackgroundColor="red"
headerTitle="Presidents"/>
...
</ec:table>
使用ExportCsvTag導出CSV。當使用CSV導出是默認的分隔符為‘,’(comma)。你可以使用
delimiter屬性來指定為其他的符號。下面為指定‘|’(pipe)為CSV分隔符的示例:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
/>
<ec:exportCsv
fileName="presidents.txt"
tooltip="Export CSV"
delimiter="|"/>
...
</ec:table>
你可以通過指定view屬性來導出其他文件格式。eXtremeTable視圖實現View接口并是 可插接的。參閱View章了解更多信息。
大多數標簽包含一系列的固定屬性,這樣那些已經實現的功能能夠被使用。然而,eXtremeTable具有一種更具彈性的架構,
你可以添加自己的標簽屬性實現更多的定制工作。此外,eXtremeTable提供了非常清晰的鉤子(hooks)允許你得到那些定制的
標簽屬性來做一些你需要的工作。
通過addExtendedAttributes()方法將擴展屬性包含到eXtremeTable里:
public void addExtendedAttributes(Export export);
如果方法被覆蓋ExportTag將調用它。你需要做的就是擴展ExportTag,覆蓋addExtendedAttributes()方法,然后添加自己
的屬性到導出對象中。
一個定制的ExportCsvTag示例如下:
public class ExportCsvTag extends ExportTag {
private String delimiter;
public String getDelimiter() {
return delimiter;
}
public void setDelimiter(String delimiter) {
this.delimiter = delimiter;
}
public void addExtendedAttributes(Export export) {
String view = export.getView();
if (StringUtils.isBlank(view)) {
export.setView(TableConstants.VIEW_CSV);
export.setImageName(TableConstants.VIEW_CSV);
}
export.addAttribute(CsvView.DELIMITER, getDelimiter());
}
}
現在你添加了屬性值到Export對象,下面是CsvView實現的一部分:
public class CsvView implements View {
public void body(TableModel model, Column column) {
Export export = model.getExportHandler().getCurrentExport();
}
}
另外,你也可以定制自己的標簽和自己的TLD文件。你不需要修改extremecomponents.tld文件。
你能象使用eXtremeTable里的標簽一樣使用自己的標簽,除了使用你自己標簽的參照。假如你的標簽參照為mycompany
并且標簽為customExport,你可以像下面一樣使用他們:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/public/demo/presidents.jsp"
title="Presidents"
>
<mycompany:customExport fileName="presidents.txt" delimiter="|"/>
...
</ec:table>
Callbacks被用于重新得到(retrieve)、過濾和排序行數據。eXtremeTable為每個callback提供了一個定制實現。
首先,載入所有的元數據,元數據為所有eXtremeTable標簽的所有屬性;接著在eXtremeTable的model中調用
execute方法。eXtremeTable使用execute方法決定如何通過調用每個callback的接口來重新得到、過濾和排序行數據。
這三個callback的接口是:RetrieveRowsCallback、FilterRowsCallback和SortRowsCallback。
callbacks為singleton并且不是線程安全的,因此不要定義任何類變量。
8.2. RetrieveRowsCallback
RetrieveRowsCallback的默認實現在servlet范圍內尋找具有名稱和TableTag
的items屬性設置相同的Beans集合。為了使用定制的callback,只要實現RetrieveRowsCallback接口,
然后使用retrieveRowsCallback屬性來指定實現類的全路徑:
<ec:table
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
retrieveRowsCallback="com.mycompany.callback.MyCustomCallback"
/>
RetrieveRowsCallback接口如下所示:
public interface RetrieveRowsCallback {
public Collection retrieveRows(TableModel model) throws Exception;
}
只有一個方法需要實現,傳入TableModel并返回一個集合,集合為Beans或Maps集合。
通過得到TableModel,就擁有了TableTag的所有元數據并能訪問Context。能夠訪問Context非常重要,
這意味著你訪問web容器的任何東西。
FilterRowsCallback的默認實現得到Beans集合,通過實現jakarta Predicate接口進行過濾,
過濾值從eXtremeTable的filter輸入框中取得。為了使用定制的callback,只要實現FilterRowsCallback接口,
然后使用filterRowsCallback屬性來指定實現類的全路徑:
<ec:table
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
filterRowsCallback="com.mycompany.callback.MyCustomCallback"
/>
FilterRowsCallback接口如下所示:
public interface FilterRowsCallback {
public Collection filterRows(TableModel model, Collection rows) throws Exception;
}
只有一個方法需要實現,傳入TableModel并返回一個集合。你只需像eXtremeTable對
每個callback的默認實現一樣來定制自己的callback。
SortRowsCallback的默認實現得到Beans集合,使用jakarta BeanComparator進行排序,
排序值當用戶點擊列頭時取得。為了使用定制的callback,只要實現SortRowsCallback接口,
然后使用sortRowsCallback屬性來指定實現類的全路徑:
<ec:table
var="pres"
action="${pageContext.request.contextPath}/presidents.run"
sortRowsCallback="com.mycompany.callback.MyCustomCallback"
/>
SortRowsCallback接口如下所示:
public interface SortRowsCallback {
public Collection sortRows(TableModel model, Collection rows) throws Exception;
}
只有一個方法需要實現,傳入TableModel并返回一個集合。你只需像eXtremeTable對
每個callback的默認實現一樣來定制自己的callback。
為了替代硬編碼eXtremeTable使用的默認屬性值,我在屬性文件中配置所有用到的屬性。
如果你需要覆蓋任何默認的設置,你可以創建自己的extremecomponents.properties文件 并設置你想改變的值。
為了設置屬性文件,你應該如下例所示在/WEB-INF/web.xml文件中聲明一個context-param,并 指定你的屬性文件的路徑:
<context-param>
<param-name>extremecomponentsPreferencesLocation</param-name>
<param-value>/org/extremesite/resource/extremecomponents.properties</param-value>
</context-param>
你可以認為屬性文件為你提供了一個對所有的eXtremeTables聲明全局設置的一個方法。
創建屬性文件的最大好處就是避免在標簽中復制、粘貼相同的屬性。典型的extremecomponents.properties文件如下所示:
table.imagePath=/extremesite/images/*.gif
table.rowsDisplayed=12
column.parse.date=yyyy-MM-dd
column.format.date=MM/dd/yyyy
column.format.currency=$###,###,##0.00
在屬性文件定義的TableTag使用最多的兩個屬性是:imagePath和rowsDisplayed。如果你不在屬性文件中聲明
這些屬性,你需要在每個eXtremeTable中添加他們。典型的表如下所示:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
imagePath="${pageContext.request.contextPath}/images/*.gif"
rowsDisplayed="12"
title="Presidents"
>
...
</ec:table>
如果在屬性文件聲明imagePath和rowsDisplayed,則表如下所示:
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/presidents.run"
title="Presidents"
>
...
</ec:table>
正如你所見,屬性文件避免了重復編碼。
在屬性文件定義的ColumnTag使用最多的兩個屬性是:parse和format。如果你不在屬性文件中聲明
這些屬性,你需要在每個eXtremeTable中添加他們。典型的列使用日期cell如下所示:
<ec:column property="dateOfBirth" cell=”date” parse=”yyyy-MM-dd” format=”MM/dd/yyyy”/>
如果在屬性文件聲明parse和format,則列如下所示:
<ec:column property="dateOfBirth" cell=”date”/>
當然你仍然可以定義parse和format屬性來覆蓋全局設置,但是大多數工程對于日期使用一致的parse
和format。需要注意屬性文件中parse.date和format.date的聲明語法。
下例為使用貨幣cell的典型列:
<ec:column property="salary" cell=”currency” format=”$###,###,##0.00”/>
如果在屬性文件聲明format,則列如下所示:
<ec:column property="salary" cell=”currency”/>
另外,你可以聲明一個定制的format并在列中通過使用列的basis來使用它,我把這想象為named屬性。因此如果你的
extremecomponents.properties文件如下所示:
table.format.myCustomDate=yy-MM-dd
那么列可以如下使用定制的format:
<ec:column property="dateOfBirth" cell="date" format=”myCustomDate”>
10.4. Advanced
Techniques
使用named屬性是我定義其他不同屬性默認值時經常使用的方法。你可能對我
使用cell="date"來指定日期cell、使用cell="currency"來指定貨幣cell或使用view="xls."來指定xls導出感到疑惑。
如果我給你展示extremetable.properties文件的一些片斷,這些就將非常清晰了。 extremetable.properties是eXtremeTable聲明默認設置的屬性文件,你可以通過使用
extremecomponents.properties文件來覆蓋它。
column.cell.date=org.extremecomponents.table.cell.DateCell
column.cell.currency=org.extremecomponents.table.cell.NumberCell
column.filterCell.droplist=org.extremecomponents.table.cell.FilterDroplistCell
table.view.xls=org.extremecomponents.table.view.XlsView
當你在列上定義cell="date"時,eXtremeTable尋找到column.cell. 屬性并將你定義的cell屬性值拼接上。
換句話說cell="date"關聯到column.cell.date=org.extremecomponents.table.cell.DateCell這條屬性。使用屬性文件
真正強大的地方在于你能夠在extremecomponents.properties文件中聲明一個定制的cell,并在ColumnTag中通過
名稱來使用它。
再使用一個實例來闡明這一點,是否記得ColumnTag章Cell節中如何調用一個名為MyCell的定制cell:
<ec:column property="firstName" cell="com.mycompany.cell.MyCell"/>
cell使用的更好方式是在屬性文件中聲明并通過名稱使用它。首先,更新extremecomponents.properties文件:
table.imagePath=/extremesite/images/*.gif
table.rowsDisplayed=12
table.cellspacing=2
column.parse.date=yyyy-MM-dd
column.format.date=MM/dd/yyyy
column.format.currency=$###,###,##0.00
column.cell.myCell=com.mycompany.cell.MyCell
現在可以通過名稱調用MyCell:
<ec:column property="firstName" cell="myCell"/>
正如你所見的這能幫助保持代碼清潔,并且這些都在一個地方定義。如果你的定制cell聲明 需要改變你只需要修改屬性文件。
為了設置資源綁定,你應該如下例所示在/WEB-INF/web.xml文件中聲明一個context-param,并 指定你的資源文件的路徑:
<context-param>
<param-name>extremecomponentsMessagesLocation</param-name>
<param-value>org/extremesite/resource/extremecomponentsResourceBundle</param-value>
</context-param>
本示例中資源文件為extremecomponentsResourceBundle,它可以為任何名或者使用已經存在的資源文件。
如果你不指定locale,則它將根據你的servlet request來決定使用哪個資源文件。
在eXtremeTable中可以通過使用TableTag的locale屬性來設置它。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/public/demo/locale.jsp"
title="table.title.president"
locale="de_DE"
>
...
</ec:table>
在這里eXtremeTable將尋找德文資源文件。
eXtremeTable使用一些全局的keys來與用戶交互,包括:狀態欄的文本信息,Rows Displayed
droplist和不同的tooltips。如果你足夠幸運,eXtremeTable已經提供了相應的語言支持
的話,那么你什么也不用擔心。否則的話,你需要申明下列keys:
statusbar.resultsFound={0} results found, displaying {1} to {2}
statusbar.noResultsFound=There were no results found.
toolbar.firstPageTooltip=First Page
toolbar.lastPageTooltip=Last Page
toolbar.prevPageTooltip=Previous Page
toolbar.nextPageTooltip=Next Page
toolbar.filterTooltip=Filter
toolbar.clearTooltip=Clear
toolbar.clearText=Clear
toolbar.firstPageText=First
toolbar.lastPageText=Last
toolbar.nextPageText=Next
toolbar.prevPageText=Prev
toolbar.filterText=Filter
column.headercell.sortTooltip=Sort By
column.calc.total=Total
column.calc.average=Average
現在僅支持英語和德語。如果你使用其他語言的話,并能提供相應的翻譯的話我將不勝感激。你可以通過
extremecomponents@gmail.com發送給我。
譯者注:我已經提供了中文和日文的資源文件。
TableTag屬性中能夠使用locale方式指定的是:imagePath和title。
在eXtremeTable中,imagePath屬性有一個特定的key:table.imagePath。你可以在你的資源文件中
設置這個key為特定語言的目錄結構。例如:德文圖片可能放在de文件夾下,那么你可以在相應的資源文件中 進行如下設置:
table.imagePath=/extremesite/images/table/de/*.gif
title有一點不同,如果你指定的title屬性值包含dot (.)并且你定義了一個資源文件,那么
eXtremeTable將尋找匹配的key。例如,如果你像下例一樣在表中指定屬性title="table.title.president":
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/public/demo/locale.jsp"
title="table.title.president"
>
...
</ec:table>
那么eXtremeTable將在屬性文件中尋找匹配的key:
table.title.president=US Pr?sidenten
ColumnTag屬性中能夠使用locale方式指定的是:format和title。
在eXtremeTable中,format屬性有一個特定的key:table.fomat.type。參考屬性文件的討論
來了解更多的細節,他們具有同樣的概念。日期和貨幣的format類型定義可能如下所示:
column.format.date=MM/dd/yyyy
column.format.currency=$###,###,##0.00
title有一點不同,如果你指定的title屬性值包含dot (.)并且你定義了一個資源文件,那么
eXtremeTable將尋找匹配的key。例如,如果你像下例一樣在列中指定屬性title="table.column.nickName":
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/public/demo/locale.jsp"
title="table.title.president"
>
<ec:row>
<ec:column property="nickName" title="table.column.nickName" />
</ec:row>
</ec:table>
那么eXtremeTable將在屬性文件中尋找匹配的key:
table.column.nickName=Spitzname
默認的情況下eXtremeTable取得所有的結果集然后處理Beans集合,這樣的好處是
你可以隨意進行排序、過濾和分頁操作。你只需要組裝Beans集合并讓eXtremeTable知道如何
引用它。這樣的操作對于小到中等數據量的結果集非常有效,當結果集很大時這將非常糟糕。 這是一個判斷,但我更喜歡描述如何做出我的技術決定。如果您認為在性能上有問題,
那么最好是使用一個profiler工具記錄并查看它。有許多開源和商業的profiler工具可以幫助
你做出最好的判斷。因此,假設我們發現了性能上存在問題,需要我們自己來處理分頁。
手動處理分頁意味著你一次只想取得一頁顯示需要的結果集。同時,你需要自己處理排序、過濾和分頁。
下面的討論是基于我假設你從數據庫中取得集合,當然同樣的原理能應用到任何地方。
這是一個重要的部分。為了得到較小的結果集,你可以創建一個普通的查詢語句,但是limit你得到的結果集。
在Sybase和SQLServer中你可以使用rowcount命令,在MySql中你可以使用limit命令。 我不知道其他數據庫怎么使用,但我確信每個數據庫都有相似的功能。
也就是說當用戶瀏覽第一頁是得到第一頁需要的 結果集,當用戶瀏覽下一頁時,再得到下一頁需要的結果集。
使用Sybase的開發人員可能會說:rowcount命令總是從第一條開始,那么當我到第二頁時我也必須 從第一條數據開始。
是的,你現在得到的是兩頁的結果集,而不需要得到所有的結果集。當你到第三頁時,你只需要得到三頁的結果集。。。。。。
其他數據庫比如MySQL,允許你精確地得到你想要的那段數據,這樣你就可以只得到當前頁面顯示需要的結果集。
為了知道用戶想如何排序和過濾,他們想瀏覽哪一頁,一頁需要顯示幾條結果,eXtremeTable有一個使用LimitFactory
創建的名為Limit的簡便接口:
首先你需要通過LimitFactory得到一個Limit實例:
Context context = new HttpServletRequestContext(request);
LimitFactory limitFactory = new TableLimitFactory(context, tableId);
Limit limit = new TableLimit(limitFactory);
Limit對象定義了limit結果集的所有方法。
TableLimitFactory具有另外一個構造函數,如果沒有指定tableId的話默認的tableId將為ec。
Context context = new HttpServletRequestContext(request);
LimitFactory limitFactory = new TableLimitFactory(context);
Limit limit = new TableLimit(limitFactory);
當你對Limit實例化時,實例化對象包含兩個對象:FilterSet和Sort。
private FilterSet filterSet;
private Sort sort;
FilterSet包含一個過濾動作(Action)和一個過濾器對象數組。
動作為TableConstants.FILTER_ACTION或TableConstants.CLEAR_ACTION。
一個過濾器包含一個property和這個過濾器的值。
private final String action;
private final Filter[] filters;
Sort對象包含property和sortOrder。sortOrder為
TableConstants.SORT_ASC或TableConstants.SORT_DESC:
private Sort sort;
設置行屬性:
limit.setRowAttributes(totalRows, DEFAULT_ROWS_DISPLAYED);
下面是設置行屬性可能用到的信息:
private int rowStart;
private int rowEnd;
private int currentRowsDisplayed;
private int page;
private int totalRows;
每個變量都有一個getter方法,我將不深入講解屬性的細節。
在你完成所有的定制工作:排序、過濾.....定制的Controller(Spring)或者Action(Struts)或者其他類似的框架后,
另外你需要創建一個callback,eXtremeTable已經提供了一個名為LimitCallback的實現。為了使用你只需要設置表
屬性:retrieveRowsCallback、filterRowsCallback和sortRowsCallback:
<ec:table
items="presidents"
retrieveRowsCallback="limit"
filterRowsCallback="limit"
sortRowsCallback="limit"
action="${pageContext.request.contextPath}/limit.run"
title="Presidents"
>
<ec:row>
<ec:column property="fullName" title="Name"/>
<ec:column property="nickName" />
<ec:column property="term" />
<ec:column property="born" cell="date"/>
<ec:column property="died" cell="date"/>
<ec:column property="career" />
</ec:row>
</ec:table>
使用callback需要做的唯一事情是傳輸集合到request,同時傳輸totalRows屬性。
totalRows表示總行數,使用PaginationCallback.TOTAL_ROWS靜態變量將易于維護。
如果JSP頁面使用了兩個(以上)eXtremeTable的話你可以利用tableId分別傳輸totalRows。
例如如果tableId名為pres,你可以如下處理:
request.setAttribute("pres", presidents);
request.setAttribute("pres_totalRows", new Integer(""+totalRows));
譯者注:關于limit使用的更詳細信息,請參考《Limit指南》。
Chapter 13. AutoGenerateColumns
大多數情況下你按照你需要的列來設計數據庫表。但是,有時候需要運行時動態生成一些列。
為了實現這點,eXtremeTable需要使用ColumnsTag并設置autoGenerateColumns屬性。
AutoGenerateColumns為singleton并且不是線程安全的,因此不要定義任何類變量。
ColumnsTag只有autoGenerateColumns這一個屬性。所有你必須做的就是實現AutoGenerateColumns接口,
并設置autoGenerateColumns屬性為類的全路徑。
<ec:table
items="presidents"
action="${pageContext.request.contextPath}/autoGenerateColumns.run"
title="Presidents"
>
<ec:columns autoGenerateColumns="org.extremesite.controller.AutoGenerateColumnsImpl"/>
</ec:table>
AutoGenerateColumns接口只有一個方法:
public void addColumns(TableModel model);
你需要做的就是添加列(columns)到model里。最簡單的示例如下:
public class AutoGenerateColumnsImpl implements AutoGenerateColumns {
public void addColumns(TableModel model) {
Iterator iterator = columnsToAdd().iterator();
while (iterator.hasNext()) {
Map columnToAdd = (Map) iterator.next();
Column column = new Column(model);
column.setProperty((String) columnToAdd.get(PROPERTY));
column.setCell((String) columnToAdd.get(CELL));
model.getColumnHandler().addAutoGenerateColumn(column);
}
}
}
示例中columnsToAdd()方法簡單返回一個包含生成列(columns)需要的所有信息的集合。
作為參考,下面是我在eXtremeComponents網站實例中使用的columnsToAdd()方法:
private List columnsToAdd() {
List columns = new ArrayList();
columns.add(columnToAdd("fullName", "display"));
columns.add(columnToAdd("nickName", "display"));
columns.add(columnToAdd("term", "display"));
columns.add(columnToAdd("born", "date"));
columns.add(columnToAdd("died", "date"));
columns.add(columnToAdd("career", "display"));
return columns;
}
private Map columnToAdd(String property, String cell) {
Map column = new HashMap();
column.put(Column.PROPERTY, property);
column.put(Column.CELL, cell);
return column;
}
另外,我想聲明的是只創建列一次。eXtremeTable為了高效,不會每行創建一列,
而是通過循環持續插入新列值到已經存在的列。記住TableModel能夠訪問Context,因此
你可以在Controller(Spring)或Action(Struts)中定義樣式(look like)并通過request傳輸集合。
所以你得AutoGenerateColumns實現只需要構建列(Columns)并添加到model.columns里。