eXtremeComponents指南

eXtremeComponents指南

eXtremeComponents指南

Jeff Johnston

版本1.0.0

本文檔允許在遵守以下兩條原則的條件下被使用和傳播: 1)不能憑藉本文檔索取任何費用 2)以任何方式(印刷物或電子版)使用和傳播時本文檔時,必須包含本版權申明

(更新中...)


定製FilterCell


1. 引言

列的filterCell屬性控制過濾器如何顯示,它和cell屬性非常相像並且也是實現Cell接口。馬上要定義的是默認的和droplist這兩個過濾器cells。 默認的是一個輸入框元素而droplist是一個下拉列表元素。當然,如果你需要進行一些定製你可以插接自己的實現。

最近,我被問到是否能夠實現一個過濾器cell,顯示已經通過別的過濾器過濾得到數據子集。答案當然是肯定的,而且這是我將在這裏示範的。通常定製的 cell可以很容易被創建,這個示例將印證這點。在這個示例裏last name列裏顯示的將是通過first name過濾後的子集。如果沒有通過 first name過濾那麼所有值都將被顯示。

1.1. 定製Droplist過濾器Cell示例

通常你只需要爲過濾器cell實現Cell接口。然而,因爲我們要創建的過濾器cell是一個下拉列表,我們可以通過擴展 FilterDroplistCell來獲得它已經提供的很多功能,FilterDroplistCell是發行包已經提供的cells之一。

我們需要覆蓋FilterDroplistCell的唯一方法是getFilterDropList()。這是整個類的全部代碼:

public class FilteredDroplistCell extends FilterDroplistCell {
private static Log logger = LogFactory.getLog(FilterDroplistCell.class);

protected List getFilterDropList(TableModel model, Column column) {
List droplist = new ArrayList();

String firstNameFilter = model.getLimit().getFilterSet().getValue("firstName");

Collection beans = model.getCollectionOfBeans();
for (Iterator iter = beans.iterator(); iter.hasNext();) {
Object bean = iter.next();
try {
String firstName = BeanUtils.getProperty(bean, "firstName");
if (StringUtils.isNotBlank(firstNameFilter) && !firstName.equals(firstNameFilter)) {
continue;
}

String lastName = BeanUtils.getProperty(bean, column.getProperty());
if ((lastName != null) && !droplist.contains(lastName)) {
droplist.add(lastName);
}
} catch (Exception e) {
logger.debug("Problems getting the droplist.", e);
}
}

Collections.sort(droplist);

return droplist;
}
}

如果你比較這個類和父類,你會發現它們只有微小的區別。

首先需要注意的是我們需要找出first name是否已經被過濾了。

String firstNameFilter = model.getLimit().getFilterSet().getValue("firstName");

然後我們需要判斷當前bean的first name值是否和first name過濾器值相同。如果相同,將當前的last name值 添加到droplist中。

String firstName = BeanUtils.getProperty(bean, "firstName");
if (StringUtils.isNotBlank(firstNameFilter) && !firstName.equals(firstNameFilter)) {
continue;
}

如果last name將添加到droplist中,我們需要檢查droplist中是否已經包含了這個值。如果沒有,我們就把它添加到droplist中。

String lastName = BeanUtils.getProperty(bean, column.getProperty());
if ((lastName != null) && !droplist.contains(lastName)) {
droplist.add(lastName);
}

爲了使用這個Cell你應該在Preferences中聲明一個別名。 當然,你可以省略這步而在JSP中提供這個Cell實現類的全路徑,但是使用Preferences更簡潔。

column.filterCell.filteredDroplist=org.extremesite.cell.FilteredDroplistCell

在ColumnTag通過設置filterCell屬性來使用FilteredDroplistCell。

<ec:column property="lastName" filterCell="filteredDroplist"/>

如果不清楚Preferences和ColumnTag定義語法請參考Preferences指南。

定製FilterRowsCallback


1. 引言

FilterRowsCallback被用來過濾傳給eXtremeTable的Beans的Collection。 FilterRowsCallback的默認實現是得到Beans或Maps的Collection,然後通過實現jakarta Predicate接口來進行過濾。當然,如果你需要進行一些定製你可以插接自己的實現。

首先聲明,本示例代碼包含一些從原包中剪切、粘貼的代碼(雖然不是很多)。在 最初的最終發行包之後,值過濾得到進一步改善使得更具複用性並更容易實現,可能和定製cell代碼行數相同。 當然,我被要求並非常樂意示範如何在當前代碼基礎上實現定製過濾。這有非常清晰的hooks實現,並且很容易實現。

本示例示範瞭如何調整代碼爲過濾器提供一個精確的比較功能。當前的實現是通過使用StringUtils.contains()方法進行模糊比較。 本示例將使用StringUtils.equals()方法。你可以按照你的需要來調整代碼進行更多定製。

1.1. 定製FilterRowsCallback示例

首先你需要做的是創建一個實現Predicate接口的定製類。Predicate要求我們實現evaluate()方法來判斷是否包含 當前bean。因爲你僅僅調整現在已有的功能,首先得到filterPredicate的源代碼(在發行包的callback包下), 拷貝到你的工程裏。然後向下面展示的一樣將 StringUtils.contains()方法修改爲StringUtils.equals()方法:

public final class ExactMatchFilterPredicate implements Predicate {
private boolean isSearchMatch(String value, String search) {

...

else if (StringUtils.equals(value, search)) {
return true;
}

...

}
}

然後我們需要實現和Predicate共同作用的FilterRowsCallback接口。再一次從發行包的callback包下拷貝ProcessRowsCallback源代碼到你的工程裏。 請參照我們創建的定製的ExactMatchFilterPredicate 類來確認僅僅實現了FilterRowsCallback和修改Predicate。

public class ExactMatchFilterRows implements FilterRowsCallback {
public Collection filterRows(TableModel model, Collection rows) throws Exception {

...

if (filtered) {
Collection collection = new ArrayList();
Predicate filterPredicate = new ExactMatchFilterPredicate(model);
CollectionUtils.select(rows, filterPredicate, collection);

return collection;
}

...

}
}

爲了使用這個FilterRowsCallback你應該在Preferences中聲明一個別名。當然,你可以省略這步而在JSP中提供這個FilterRowsCallback實現類的全路徑,但是使用Preferences更簡潔。

table.filterRowsCallback.exactMatch=org.extremesite.callback.ExactMatchFilterRows

在TableTag通過設置filterRowsCallback屬性來使用ExactMatchFilterRows。

<ec:table filterRowsCallback="exactMatch"/>

如果不清楚Preferences和ColumnTag定義語法請參考Preferences指南。

Form指南


1. 引言

eXtremeTable本質上是一個form組件,所以我假定表被包在form裏,所有的功能都被認爲是對form元素的操作。如果你想在表體中包含一些定製的form元素, 或者想將eXtremeTable嵌入到另外的form中,那麼你就要使用表標籤的form屬性用來參照最近的form。

爲了示範form特性,我們要做的工作將分解爲JSP,Cell和Controller。

1.1. JSP

下面列出的是checkbox示例的完整代碼。想要強調的主要事情是表標籤form屬性設置爲presForm,它參照被稱爲presForm的form元素。

同時請注意表標籤的autoIncludeParameters屬性。進行排序、過濾、分頁時,默認的eXtremeTable將保持所有傳至JSP頁面的參數。 這個特性對於內部其他的form進行排序、過濾、分頁時,用於高效複製form元素同樣有效。可以設置 autoIncludeParameters屬性爲false來固定它。

在這個form使用id屬性是因爲xhtm標準的要求,同時你也可以使用form的name屬性。

<form id="presForm" action="<c:url value="selectedPresidentsListedController.run"/>" method="post">

Enter your name:
<input
type="text"
name="userName"
style="font-family:verdana,arial,helvetica,sans-serif;font-size:11px;"
value="<c:out value="${param.userName}"/>"
/>

<ec:table
items="presidents"
action="${pageContext.request.contextPath}/selectedPresidentsController.run"
view="compact"
imagePath="${pageContext.request.contextPath}/images/table/compact/*.gif"
rowsDisplayed="8"
autoIncludeParameters="false"
form="presForm"
>
<ec:exportPdf
fileName="output.pdf"
tooltip="Export PDF"
headerColor="black"
headerBackgroundColor="#b6c2da"
headerTitle="Presidents"
/>
<ec:row>
<ec:column
alias="checkbox"
title=" "
width="5px"
filterable="false"
sortable="false"
viewsAllowed="compact"
cell="selectedPresident"
/>
<ec:column property="fullName" title="Name"/>
<ec:column property="nickName" />
<ec:column property="term" />
</ec:row>
</ec:table>

<input
type="button"
name="sel"
class="button"
value="List Selected Presidents"
onclick="document.forms.presForm.submit();"
/>

<script type="text/javascript">
function setPresidentState(chkbx) {
//make sure that always know the state of the checkbox
if (chkbx.checked) {
eval('document.forms.presForm.chkbx_' + chkbx.name).value='SELECTED';
} else {
eval('document.forms.presForm.chkbx_' + chkbx.name).value='UNSELECTED';
}
}
</script>

</form>

1.1.1. Form特性的技術說明

表標籤form屬性參照最近的form是你使用這個特性所必須知道的,爲了更好的理解這個特性,介紹更多的關於內部實現技術的細節是值得的。

如果您不特意指定form屬性,eXtremeTable自動在表附近包上一個form。所有表的動作例如:排序、過濾、分頁將自動給一些隱藏的input元素賦值,然後提交這個form到表標籤action屬性設置的Aciton。 這非常有效,除非您想要將自己的form元素設置到表體,或者想將這個表放到別的form裏。

表標籤form屬性參照最近的form,所有表的動作例如:排序、過濾、分頁將自動給一些隱藏的input元素賦值,但是現在 最近form的action屬性將要改變表標籤的動作。這非常重要,因爲:當排序、過濾、分頁時,eXtremeTable能夠從一個controller得到數據集合 ,但是提交這個form到別的controller來處理這個form時需要對用戶的輸入進行處理。然而,這些對於你使用表標籤來說都是透明的。 就像你現在做的那樣簡單地設置表標籤的action屬性,然後設置相關的form到你想提交的位置。

1.1.2. Checkbox

示例的第一列是checkbox。因爲這列不需要參照bean的屬性,alias屬性用來唯一地標識這列。你可以使用property 屬性,但是alias屬性使這列如何使用更清楚。alias屬性還被用來當同樣的屬性被多列使用時唯一地標識一列。

1.1.3. Custom Cell

您也許想知道定製的cell是如何通過名稱selectedPresident被參照的(cell="selectedPresident")。這是一個 對eXtremeTable的preferences特性更強的使用。所有要做的就是在extremecomponents.properties文件中添加一個屬性。 請參考Preferences來了解更多的信息

column.cell.selectedPresident=org.extremesite.cell.SelectedPresidentCell

column.cell.selectedPresident就是你定義的用來參照這個cell的名稱。

當然你也可以使用這個Cell的全名來進行參照。

<ec:column 
alias="checkbox"
title=" "
width="5px"
filterable="false"
sortable="false"
viewsAllowed="compact"
cell="org.extremesite.cell.SelectedPresidentCell"
/>

在屬性文件中定義參照更方便,它可以被任何JSP文件引用。如果類名或包名改變的話你只需要對一個地方進行修改。

1.1.4. JavaScript

JavaScript的setPresidentState()方法被定製cell用來設置每個checkbox元素的是否被選中。 設置一個隱藏元素的原因是爲了獲得瀏覽器的動作而不提交沒有選中的checkbox。通過這個Controller將一直知道一個元素是否別選中。

1.2. Cell

定製的cell被用來生成checkbox,另外它也創建一個隱藏元素用來表示這個checkbox元素是否被選中。 當用戶進行排序、過濾、分頁時,被選中的數據集合將被放到session裏。

getExportDisplay()方法沒有返回值,因爲治理只需要Html顯示。

public class SelectedPresidentCell implements Cell {
public String getExportDisplay(TableModel model, Column column) {
return null;
}

public String getHtmlDisplay(TableModel model, Column column) {
HtmlBuilder html = new HtmlBuilder();

CellBuilder.tdStart(html, column);

try {
Object bean = model.getCurrentRowBean();
String presidentId = BeanUtils.getProperty(bean, "presidentId");

Collection selectedPresidentsIds = (Collection)model.getContext().getSessionAttribute(SelectedPresidentsConstants.SELECTED_PRESIDENTS);
if (selectedPresidentsIds != null && selectedPresidentsIds.contains(presidentId)) {
html.input("hidden").name("chkbx_" + presidentId).value(SelectedPresidentsConstants.SELECTED).xclose();
html.input("checkbox").name(BeanUtils.getProperty(bean, "presidentId"));
html.onclick("setPresidentState(this)");
html.checked();
html.xclose();
} else {
html.input("hidden").name("chkbx_" + presidentId).value(SelectedPresidentsConstants.UNSELECTED).xclose();
html.input("checkbox").name(BeanUtils.getProperty(bean, "presidentId"));
html.onclick("setPresidentState(this)");
html.xclose();
}
} catch (Exception e) {}

CellBuilder.tdEnd(html);

return html.toString();
}
}

1.3. Controller

提示:Spring框架的Controller和Struts框架的Action非常相像。

當在另外的form中使用eXtremeTable時,你可能有1個或2個controllers。當form被提交時,你需要一個controller 來處理用戶的輸入並重新定向到另外的JSP頁面。當排序、過濾、分頁時,你可能有另外的controller來得到表使用的數據集合並重定向會本頁。或者你可以在同一個controller中分別處理。

checkbox示例裏我使用一個controller來關聯表標籤的action屬性。我也使用另外一個controller來關聯form元素的動作。

1.3.1. 表標籤動作Controller

這個controller負責調用SelectedPresidentsUtils來保存被選中的presidentIds到session裏並回到同一頁。

SelectedPresidentsUtils.saveSelectedPresidentsIDs(request);
Collection presidents = presidentsService.getPresidents();
request.setAttribute("presidents", presidents);

1.3.2. Form動作Controller

這個controller負責通過presidentIds得到數據集並重定向到下一個Jsp頁面

Collection selectedPresidentsIds = SelectedPresidentsUtils.saveSelectedPresidentsIDs(request);
Collection selectedPresidents = SelectedPresidentsUtils.getSelectedPresidents(presidentsService.getPresidents(), selectedPresidentsIds);
request.setAttribute("selected", selectedPresidents);
request.getSession().removeAttribute(SelectedPresidentsConstants.SELECTED_PRESIDENTS);

1.3.3. 重新得到Checkbox的值

我將列出保存presidentIds到session的代碼。我經常被問到如何重新得到eXtremeTable中form元素的值。 它的原理是設置form輸入元素名字屬性值前面加上一些東西來唯一標識元素

本示例中我關心的是以chkbx開頭參數的元素。chkbx後面是唯一的關聯到checkbox的presidentId。它被用來判斷這個checkbox是否別選中。

public static Collection saveSelectedPresidentsIDs(HttpServletRequest request) {
Collection presidents = (Collection) request.getSession().getAttribute(SelectedPresidentsConstants.SELECTED_PRESIDENTS);

if (presidents == null) {
presidents = new ArrayList();
request.getSession().setAttribute(SelectedPresidentsConstants.SELECTED_PRESIDENTS, presidents);
}

Enumeration parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String parameterName = (String) parameterNames.nextElement();
if (parameterName.startsWith("chkbx_")) {
String presidentId = StringUtils.substringAfter(parameterName, "chkbx_");
String parameterValue = request.getParameter(parameterName);
if (parameterValue.equals(SelectedPresidentsConstants.SELECTED)) {
if (!presidents.contains(presidentId)) {
presidents.add(presidentId);
}
} else {
presidents.remove(presidentId);
}
}
}

return presidents;
}

Html視圖定製指南


1. 引言

eXtremeTable使用View接口來生成HTML。你可以使用發行包已經提供的視圖,或者你可以插入自己的視圖實現。 現在,創建你自己的視圖相對比較簡單,但討論一些設計想法和如何着手實現一個定製的視圖還是有價值的。

我想使創建定製視圖簡單,但不是想構造一個更復雜的類似swing的模型,原因是那需要創建大量的對象來處理對應的內部工作。 eXtremeTable以高效爲目標,我也想在視圖的實現上貫徹這種想法,所以我決定創建一系列的靜態構造器類來實現分解的最小功能。你可以通過組合這些功能來實現你的定製視圖。

學習定製視圖的最好途徑是閱讀已經存在的視圖的源代碼,修改它來滿足你的需求。如果我示範所有東西的話,這篇指南將變的非常冗長。取而代之的是我將直接修改默認視圖的工具條作爲定製視圖的一個示例。 對於不同構造器的具體細節我建議你閱讀源代碼。我也將盡量更新javadocs來提供更好的幫助。

1.1. View接口

實現View接口的類有3次插入內容的機會。beforeBody()方法會被立刻調用,body()方法在每一行的每一列處理的時候調用。 afterBody()方法是被eXtremeTable調用的最後方法,它將返回代表視圖的一個對象。在這個HTML視圖示例裏,它將是一個字符串。

public interface View {
public void beforeBody(TableModel model);
public void body(TableModel model, Column column);
public Object afterBody(TableModel model);
}

1.2. Messages示例

在這篇指南里我將直接修改工具條來實現這網站上Messages示例的定製視圖。

public class MessagesView extends AbstractHtmlView { 
protected void toolbar(TableModel model) {
TwoColumnTableLayout toolbar = new MessagesToolbar();
toolbar.layout(getHtmlBuilder(), model);
}

protected void statusBar(TableModel model) {
TwoColumnRowLayout statusBar = new MessagesStatusBar();
statusBar.layout(getHtmlBuilder(), model);
}
}

這裏使用的是默認視圖,因此它擴展了虛擬視圖來修改工具條和狀態條。如何修改工具條和(或)狀態條也是開發人員問的最多問題。

默認視圖的工具條位於表的上方包括翻頁鏈接和標題。工具條使用TwoColumnTableLayout,它是一個用於提供在自己表中實現左右兩列布局的虛擬類。 它將實現能夠浮在表上方的完美佈局。下面就是你需要關心的虛擬方法,在實際的html視圖中已經爲你完成了這個佈局。

public abstract class TwoColumnTableLayout {
protected abstract boolean showLayout(TableModel model);
protected abstract void columnLeft(HtmlBuilder html, TableModel model);
protected abstract void columnRight(HtmlBuilder html, TableModel model);
}

showLayout()方法用來阻止或導致佈局的展現。在我的定製視圖中如果翻頁或(和)導出顯示那麼工具條將展現。

protected boolean showLayout(TableModel model) {
boolean showPagination = BuilderUtils.showPagination(model);
boolean showExports = BuilderUtils.showExports(model);
if (!showPagination && !showExports) {
return false;
}

return true;
}

下面顯示了左列和右列的部分代碼。注意在我的定製視圖中首頁和前一頁使用了文字來替代圖片顯示。我真正希望示範的是你需要做的:找到正確的構造器類並且僅僅是擴展HtmlBuilder的標籤。 構造器類對於示範如何找到模型裏的信息(以便你能夠做比他們能夠提供的更多的定製工作)也非常有用,。

protected void columnLeft(HtmlBuilder html, TableModel model) {
html.td(2).close();
TableBuilder.title(html, model);
html.tdEnd();
}

protected void columnRight(HtmlBuilder html, TableModel model) {
boolean showPagination = BuilderUtils.showPagination(model);
...
if (showPagination) {
html.td(4).close();
ToolbarBuilder.firstPageItemAsText(html, model);
html.tdEnd();

html.td(4).close();
ToolbarBuilder.prevPageItemAsText(html, model);
html.tdEnd();
...
}
...
}

爲了使用這個視圖你需要在Preferences定義一個別名。 你可以省略這部而在JSP直接給出這個視圖的完整有效的類名,不過Preferences更爲簡潔。

table.view.messages=org.extremesite.view.MessagesView

TableTag也將設置視圖屬性來使用MessagesView視圖。

<ec:table view="messages">

如果不清楚Preferences和TableTag定義語法請參考Preferences指南。

攔截器使用指南


1. 引言

攔截特性被用在運行時需要修改屬性值的時候,它使得改變基於數據的eXtremeTable的行爲成爲可能。在閱讀擴展標籤屬性時,你會發現它和擴展標籤屬性具有同樣的概念和方法標識。 區分使用他們的首要準則是:如果需要向TLD裏已經定義的並且能夠在JSP中訪問的標籤添加新的屬性時,應該使用擴展標籤屬性;如果僅僅是需要修改已經定義好的屬性的值的時候,應該使用攔截器。

你可能需要了解更多的eXtremeTable如何運作的技術背景才能完全理解這種特性。 eXtremeTable首先做的就是遍歷所有標籤並創建對應的模型beans (pojos)。beans是具有和標籤一樣屬性,但是使用真實類型來替換僅僅使用字符串類型的對象。beans是被模型使用並且是你需要使用攔截特性修改的對象。 所有的攔截器接口都定義了一個add方法, add方法被用來處理模型bean第一次創建時的屬性。行和列的攔截器還有一個modify 方法。modify方法可以在當行和類進行處理是對屬性值進行操作。

1.1. 攔截器列表

下面列出了具有攔截特性的標籤和他們需要被實現的接口,Bean欄顯示了被模型創建的Bean。

標籤 接口 Bean
TableTag org.extremecomponents.table.intercept.InterceptTable org.extremecomponents.table.bean.Table
RowTag org.extremecomponents.table.intercept.InterceptRow org.extremecomponents.table.bean.Row
ColumnTag org.extremecomponents.table.intercept.InterceptColumn org.extremecomponents.table.bean.Column
ExportTag org.extremecomponents.table.intercept.InterceptExport org.extremecomponents.table.bean.Export

1.2. 行攔截器示例

示範攔截特性的完美示例就是根據一定的標準來對行進行高亮顯示,這也是我們將要完成的示例。它很短也很簡單,不過它實現的概念同樣適用於每一個攔截器接口。

我們需要做的第一件事就是實現InterceptRow接口。你會注意到這個接口有兩個方法:addRowAttributes() 和modifyRowAttributes()。addRowAttributes方法在行bean創建的時候被調用, modifyRowAttributes方法在表處理當前頁面行的時候被調用。

public class MarkerIntercept implements InterceptRow {
public void addRowAttributes(TableModel tableModel, Row row) {
}

public void modifyRowAttributes(TableModel model, Row row) {
President president = (President) model.getCurrentRowBean();
String career = president.getCareer();
if (StringUtils.contains(career, "Soldier")) {
row.setStyle("background-color:#fdffc0;");
} else {
row.setStyle("");
}
}
}

在Preferences裏你應該定義這個行攔截器的別名。

row.intercept.marker=org.extremesite.intercept.MarkerIntercept

這樣就可以在行標籤中使用攔截器MarkerIntercept了。

<ec:row intercept="marker">

如果不清楚Preferences和TableTag定義語法請參考Preferences指南。

Limit指南


1. 引言

在你需要處理大量數據時你應該考慮使用eXtremeTable的Limit特性。Limit這個名字來自MySQL的limit 命令,Limit接口的目的就是如何對錶的結果集進行limit處理。Limit實現知道當排序、過濾、分頁、導出時,用戶如何與表互相作用。有了這些信息你 將能夠使用可能是最有效的方式顯示正確的過濾、排序後的請求頁面。

爲了示範Limit特性,我將要做的工作將分解爲JSP、Controller、Service和DAO。這示範了一種使用分層的方式來處理 Limit。你可以根據自己的需要來增加或減少層。本示例也使用了Spring框架來重新得到使用Spring的JDBC取得的數據,因此你的代碼看起來可能有點不同。eXtremeTable的一個特點就是不依賴任何框架和容器。

1.1. JSP

爲了使用Limit特性,eXtremeTable需要使用limit特定的RetrieveRowsCallback、 FilterRowsCallback和SortRowsCallback接口。eXtremeComponents提供了每個接口的一個實現,可以簡單地通過設置每個屬性值爲limit來簡單來使用。

<ec:table 
items="presidents"
retrieveRowsCallback="limit"
filterRowsCallback="limit"
sortRowsCallback="limit"
view="limit"
>
...

另外視圖屬性參照一個名爲limit的定製視圖。這是一個簡單修改默認eXtremeTable視圖,不包含最後頁工具條的實現。這僅僅關係到你是否能取得確切需要的行。 一些數據庫例如Oracle和MySQL都提供了一種得到確定行的特性,但是,其他的數據庫例如:Sybase沒有提供特性。在我的示例中我考慮最壞的情況你的數據庫不支持這種特性。

即使你的數據庫不提供取得特定行的特性,當你考慮用戶如何和表協同工作時,Limit仍然非常有意義。用戶通常會對一些數據進行排序、過濾和分頁。 這個例子中15條數據構成一頁,第一頁需要15條數據,第二頁需要30條數據,第三頁需要45條數據,以此類推。在經過一段時間分頁後,他們常常使用過濾來提煉數據。 即使他們不這樣做,他們也必須在此之前對大量的數據進行分頁,這將影響效率。當然如果允許用戶點擊最後頁,那麼所有的數據都將被取出,這將非常影響效率。

1.2. Controller

提示:Spring框架的Controller和Struts框架的Action非常相像。

controller首先需要創建一個Limit。爲了完成這個你需要得到一些關於Context和LimitFactory的幫助。

Context context = new HttpServletRequestContext(request);
LimitFactory limitFactory = new TableLimitFactory(context);
Limit limit = new TableLimit(limitFactory);

Context是一個處理取得屬性的接口,LimitFactory使用Context來找出用戶如何和eXtremeTable交互。 然後Limit使用LimitFactory來組裝自己。

爲了初始化Limit,它將包含所有的有用的信息。這些信息包括數據將被如何排序和過濾,哪一頁將被顯示和是否允許被導出。

然而,Limit仍然需要得到行的信息,這樣正確的信息頁面才能被顯示給用戶。行信息包括開始行、結束行、當前顯示行。 controller必須從service得到這些信息,而Service將從dao中得到這些信息。這裏我只給出Controller端的代碼。

int totalRows = presidentsService.getTotalPresidents(limit.getFilterSet(), limit.isExported());
limit.setRowAttributes(totalRows, defaultRowsDisplayed);

limit需要得到所有的行來得到行的信息。service需要知道那些被過濾,不管這些數據是否要導出。爲了設置行信息,默認的一頁顯示的行數需要被設置。 這可以通過對TableTag的rowsDisplayed屬性設置一個確定的數值來實現。

現在我們只需要從services得到Collection數據。

Collection presidents = presidentsService.getPresidents(limit.getFilterSet(), limit.getSort(), limit.getRowEnd());

因爲limit已經包含所有信息,這將十分容易。所有需要做的就是傳入過濾器,排序和最後行的信息。 最後要做的是將Collections和totalRow這些信息傳送回JSP以便eXtremeTable知道如何顯示這些信息。

request.setAttribute("presidents", presidents);
request.setAttribute("totalRows", new Integer(totalRows));

1.3. Service

service需要和dao進行交互來得到總行數和Collection。

1.3.1. 取得總行數

controller需要到第一條信息就是總行數。

public int getTotalPresidents(FilterSet filterSet, boolean isExported) {
String totalQuery = presidentsDao.getTotalPresidentsQuery();
String modTotalQuery = filterQuery(filterSet, totalQuery);
int totalRows = presidentsDao.getTotalPresidents(modTotalQuery);
if (isExported && totalRows > maxExportRows) {
totalRows = maxExportRows;
}
return totalRows;
}

service和dao一起來過濾結果集,它的工作方式是在Where語句後面增加更多的AND語句來修改查詢字符串。爲此,你需要和Limit FilterSet一起工作。

FilterSet是一個過濾器對象數組,一個過濾器包括一個bean property和這個過濾器的值。或者,簡單的說就是用戶想要過濾的行和他們輸入的值。這使得它非常容易交互。service只需要迭代所有的 FilterSet並調用dao來拼接查詢語句。(譯者注:過濾的實現方式是:在Where後面增加And語句來改變查詢語句以達到對數據進行過濾的效果)

private String filterQuery(FilterSet filterSet, String query) {
if (!filterSet.isFiltered() || filterSet.isCleared()) {
return query;
}

Filter filters[] = filterSet.getFilters();
for (int i = 0; i < filters.length; i++) {
Filter filter = filters[i];
String property = filter.getProperty();
String value = filter.getValue();
query = presidentsDao.filterQuery(query, property, value);
}

return query;
}

query修改包括了filter信息,總行數。在一些情況下這就足夠,但是當用戶導出數據時仍然存在一個潛在的問題。爲了保持高效 service不允許導出超出一個最大行數的數據。

1.3.2. 取得Collection

controller需要到第二條信息就是Collection。

public Collection getPresidents(FilterSet filterSet, Sort sort, int rowEnd) {
String patientsQuery = presidentsDao.getPresidentsQuery();
String modPatientsQuery = filterQuery(filterSet, patientsQuery);
modPatientsQuery = sortQuery(sort, modPatientsQuery);
modPatientsQuery = presidentsDao.limitQuery(rowEnd, modPatientsQuery);
return presidentsDao.getPresidents(modPatientsQuery);
}

和前面一樣,service和dao一起來過濾結果集。

另外query字符串需要擴展ORDER BY語句以便數據按照正確的方式進行排序。Sort包含一個bean property和 sortOrder值(正序還是逆序)。service僅僅需要使用Sort來調用dao。

private String sortQuery(Sort sort, String query) {
if (!sort.isSorted()) {
String defaultSortOrder = presidentsDao.getDefaultSortOrder();
if (StringUtils.isNotBlank(defaultSortOrder)) {
return query + defaultSortOrder;
}

return query;
}

String property = sort.getProperty();
String sortOrder = sort.getSortOrder();

return presidentsDao.sortQuery(query, property, sortOrder);
}

query字符串最後需要的修改就是增加數據庫特別的指令來limit將要被返回的結果集。這就是limitQuery() 方法的作用。

1.4. DAO

dao爲service負責底層數據工作。

1.4.1. 定義Query字符串

爲了真正理解dao,query字符串需要被展示。

這就是得到數據的presidents query字符串:

private final static String presidentsQuery = 
" SELECT " +
" president_id presidentId, " +
" first_name firstName, " +
" last_name lastName, " +
" nick_name nickName, " +
" concat(first_name, ' ',last_name) fullName, " +
" term, " +
" born, " +
" died, " +
" education, " +
" career, " +
" political_party politicalParty " +
" FROM presidents ";

這是得到總行數的query字符串:

private final static String totalPresidentsQuery = 
" SELECT count(*) FROM presidents ";

1.4.2. Filter 和 Sort Query 字符串

兩個最有趣的方法就是過濾和排序。

filter看起來像這樣:

public String filterQuery(String query, String property, String value) {
StringBuffer result = new StringBuffer(query);

if (query.indexOf("WHERE") == -1) {
result.append(" WHERE 1 = 1 "); //stub WHERE clause so can just append AND clause
}

if (property.equals("fullName")) {
result.append(" AND concat(first_name, ' ',last_name) like '%" + value + "%'");
} else if (property.equals("nickName")) {
result.append(" AND nick_name like '%" + value + "%'");
} else {
result.append(" AND " + property + " like '%" + value + "%'");
}

return result.toString();
}

filterQuery()方法需要增加正確的AND語句到query字符串。

sort看起來非常類似:

public String sortQuery(String query, String property, String sortOrder) {
StringBuffer result = new StringBuffer(query + " ORDER BY ");

if (property.equals("fullName")) {
result.append("concat(first_name, ' ',last_name) " + sortOrder);
} else {
result.append(property + " " + sortOrder);
}

return result.toString();
}

sortQuery()方法需要增加正確的ORDER BY語句到query字符串。

1.4.3. Limit Query String

現在query字符串修改能夠正確的進行filter和sort,它還需要修改以便只取頁面顯示相關的數據。MySQL爲s the limit命令。

public String limitQuery(int rowEnd, String query) {
return query + " limit " + rowEnd;
}

1.4.4. 取回總行數和Collection.

service需要的唯一東西就是:總行數和Collection。

public Collection getPresidents(final String query) {
return jdbcTemplate.query(query, new ResultReader() {
List results = new ArrayList();
public List getResults() {
return results;
}

public void processRow(ResultSet rs)
throws SQLException {
President president = new President();
president.setPresidentId(new Integer(rs.getInt("presidentId")));
president.setFirstName(rs.getString("firstName"));
president.setLastName(rs.getString("lastName"));
president.setNickName(rs.getString("nickName"));
president.setFullName(rs.getString("fullName"));
president.setTerm(rs.getString("term"));
president.setBorn(rs.getDate("born"));
president.setDied(rs.getDate("died"));
president.setEducation(rs.getString("education"));
president.setCareer(rs.getString("career"));
president.setPoliticalParty(rs.getString("politicalParty"));
results.add(president);
}
});
}

public int getTotalPresidents(final String query) {
return jdbcTemplate.queryForInt(query);
}

ResultReader是一個幫助處理JDBC查詢的Spring特殊類,作爲一個callback來處理JDBC ResultSet。jdbcTemplate是對JDBC連接的抽象。

1.4.5. 默認的Sort順序

最後,這是service需要的默認sort順序:

public String getDefaultSortOrder() {
return " ORDER BY concat(first_name, ' ', last_name) ";
}

Preferences 指南


1. 引言

爲了設置全局屬性和設置,你需要使用Preferences特性,它現在使用一個屬性文件來實現。本文檔將很好地介紹如何在web.xml裏設置Preferences, 以及一些需要被定義的通用屬性。在這裏我非常樂意介紹一些關於Preferences的進一步用法。

所有標籤屬性表示一個可插接的接口,它可以通過給出實現的全路徑來設置。這爲插接實現提供了一條便利的途徑。當然這存在一些爲過長術語的設計和維護的考慮。 第一,對你的接口實現進行硬編碼;第二,如果你需要在別的JSP中用到同一個接口實現,你需要拷貝你全路徑。解決這兩個問題的有效辦法就是在Preferences中聲明一切。

1.1. Preferences表

下面列出的是可以在Preferences中申明的所有接口。Tag列展示的是eXtremeTable的標籤,Attribute 列展示的是相關標籤的對應屬性。Interface列展示的是需要被實現的Java接口。Preference Key列展示的是 Preferences裏對應的健。

Tag Attribute Interface Preference Key
TableTag filterRowsCallback org.extremecomponents.table.callback.FilterRowsCallback table.filterRowsCallback
TableTag intercept org.extremecomponents.table.intercept.InterceptTable table.intercept
TableTag retrieveRowsCallback org.extremecomponents.table.callback.RetrieveRowsCallback table.retrieveRowsCallback
TableTag sortRowsCallback org.extremecomponents.table.callback.SortRowsCallback table.sortRowsCallback
TableTag state org.extremecomponents.table.state.State table.state
TableTag view org.extremecomponents.table.view.View table.view
RowTag intercept org.extremecomponents.table.intercept.InterceptRow row.intercept
ColumnTag calc org.extremecomponents.table.calc.Calc column.calc
ColumnTag cell org.extremecomponents.table.cell.Cell column.cell
ColumnTag filterCell org.extremecomponents.table.cell.Cell column.filterCell
ColumnTag headerCell org.extremecomponents.table.cell.Cell column.headerCell
ColumnTag intercept org.extremecomponents.table.intercept.InterceptColumn column.intercept
ExportTag intercept org.extremecomponents.table.intercept.InterceptExport export.intercept
ExportTag view org.extremecomponents.table.view.View export.view
ExportTag viewResolver org.extremecomponents.table.filter.ViewResolver export.viewResolver

提示:當在寫作本指南的時候,我意識到我忘記了讓標籤ColumnsTag的autoGenerateColumns 屬性和Preferences協同工作。這將在下一版修正。

1.2. 指定Preference別名

上表展示瞭如何聲明preference鍵,但是沒有解釋如何指定有意義的別名。如果你注意到preference鍵提供了一致的語法 tag.attribute,指定鍵的別名僅僅是在它的基礎上進行擴展。它的語法爲: tag.attribute.alias

eXtremeTable提供了一個名爲RowCountCell定製的cell,它的作用是現實當前的行數。我將在Preferences裏使用ColumnTag cell聲明來示範RowCountCell的使用。

首先通過實現Cell接口或者擴展AbstractCell來編寫具體的實現類。

public class RowCountCell extends AbstractCell {
protected String getCellValue(TableModel model, Column column) {
int rowcount = ((model.getLimit().getPage() - 1)
* model.getLimit().getCurrentRowsDisplayed())
+ model.getRowHandler().getRow().getRowCount();
return String.valueOf(rowcount);
}
}

然後在Preferences (屬性文件)進行聲明並給出別名。eXtremeTable在一個Preferences裏保存所有的配置信息,你可以通過使用本地 Preferences的來覆蓋任何的這些屬性。

RowCountCell默認的別名是rowCount:

column.cell.rowCount=org.extremecomponents.table.cell.RowCountCell

在ColumnTag中通過別名引用Cell:

<ec:column alias="count" cell="rowCount"/>

現在你可以通過rowCount來引用這個Cell,如果包名改變了你只需要對Preferences進行修改。

提示:本示例中我使用了ColumnTag的別名屬性。別名屬性應用在有兩列使用同樣的property,也應用在列不直接和列的 bean property關聯的情況下。本示例就屬於這種情況。

posted on 2006-02-26 10:00 Lucky 閱讀(1047) 評論(14)  編輯 收藏 收藏至365Key 所屬分類: extremeComponents

評論:
# re: extremeComponents指南 2006-03-04 10:33 | 一滴水
正好用上,謝謝:)  回覆
  
# re: extremeComponents指南 2006-03-04 10:47 | Lucky
不客氣,希望能爲大家帶來方便。現在我正在熟悉它的源代碼,Jeff讓我幫着添加一些小的特性(feature),希望大家有什麼想法、建議...,請和我([email protected])聯繫。  回覆
  
# 提一個小bug 2006-04-13 15:18 | snowolf
翻頁按鈕提示是通過圖片的title屬性實現國際化的,但圖片又定義了alt,使用的英文,當這個按鈕沒有鏈接的時候,title就沒有定義,顯示的提示就是alt的內容。  回覆
  
# re: extremeComponents指南 2006-04-13 15:54 | xplucky
@snowolf
我自己測試了一下,並沒有出現你說的那種情況啊?你用的extremeComponents的版本是多少?你能不能提供更詳細一點的情況,或者抓副圖mail給我。在線聯繫也行,我的MSN: [email protected]  回覆
  
# re: extremeComponents指南 2006-04-13 18:33 | snowolf
我用的是1.0.1M4,從sf上下的,我看你提到m5,我想要到cvs裏去下吧
我碰到的bug,舉個例子說,列表有多頁,現在處在第一頁,那麼按鈕第1頁(First),前一頁(Prev)這個時候是沒有鏈接的,下一頁(Next)、最後一頁(Last)是有鏈接的,這時候我把鼠標放在這些按鈕上時,會出現一個提示信息,
First、Prev是英文的,Next、Last是中文的。
生成的html代碼是這樣的
<td><img src="/eXtremeTable/images/table/firstPageDisabled.gif" style="border:0" alt="First" /></td>
<td><img src="/eXtremeTable/images/table/prevPageDisabled.gif" style="border:0" alt="Prev" /></td>
<td><a href="javascript:document.forms.ec.ec_p.value='2';document.forms.ec.action='/eXtremeTable/test.jsp';document.forms.ec.submit()"><img src="/eXtremeTable/images/table/nextPage.gif" style="border:0" title="下一頁" alt="Next" /></a></td>
<td><a href="javascript:document.forms.ec.ec_p.value='2';document.forms.ec.action='/eXtremeTable/test.jsp';document.forms.ec.submit()"><img src="/eXtremeTable/images/table/lastPage.gif" style="border:0" title="最後頁" alt="Last" /></a></td>

注意裏面img裏的title屬性,當按鈕有鏈接時,纔會有這個屬性

我剛到springside裏看到這個用的是正常的,他用的是我下的這個版本的改造版(改造了excel輸出的中文問題)
不知道是不是我配置的問題,鬱悶  回覆
  
# re: extremeComponents指南 2006-04-13 18:37 | snowolf
問題我想找到了,是資源文件的原因
我把springside裏面改過的資源文件拿過來就行了
裏面多了幾行
toolbar.firstPageText=/u7b2c/u4e00/u9875
toolbar.lastPageText=/u6700/u540e/u9875
toolbar.nextPageText=/u4e0b/u4e00/u9875
toolbar.prevPageText=/u4e0a/u4e00/u9875
  回覆
  
# re: extremeComponents指南 2006-04-13 18:40 | snowolf
原來**PageText纔是定義alt的,沒看源代碼,推測出的問題出處有誤,不好意思,麻煩你了  回覆
  
# re: extremeComponents指南 2006-04-13 18:41 | xplucky
你還在線嗎?M4版的中文資源文件比英文的少了一下東西。  回覆
  
# re: extremeComponents指南 2006-04-13 18:44 | xplucky
如果你願意嘗試,我可以給你最新的jar。有一些朋友已經再使用了,還沒有收到太大問題的反饋。  回覆
  
# re: extremeComponents指南 2006-04-14 11:21 | snowolf
好啊,麻煩你發到我的郵箱snowolf9929(at)163.com 謝謝

我現在正在爲新項目選一些組件和標籤,剛開始看到displaytag,看到網上一些評論,發現eXtremeTable很不錯,運行裏面的test感受了一下,也嘗試修改裏面的一些屬性,看到你翻譯的文章,介紹裏面有很多地方可以自己擴展,這種設計確實不錯,還沒嘗試去做一個擴展的例子,感覺人還是挺懶,能不擴展就不擴展了,呵呵。

現在開源的東西很多,選用哪種也很令人頭疼啊。。。  回覆
  
# re: extremeComponents指南 2006-04-14 11:41 | xplucky
@snowolf
我已經給你的163信箱發了,不過點的快了點,沒加標題,不好意思。  回覆
  
# re: extremeComponents指南 2006-06-17 15:17 | sfd
extremeComponents的單元格能不能編輯!

  回覆
  
# re: extremeComponents指南 2006-06-18 18:02 | Lucky
到目前爲止,還不具備編輯功能!  回覆
  
# re: extremeComponents指南 2006-06-19 12:53 | sfd
能不能加上水平,垂直滾動條!  回覆
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章