顯示具有 Displaytag 標籤的文章。 顯示所有文章
顯示具有 Displaytag 標籤的文章。 顯示所有文章

2008年10月12日 星期日

[Displaytag] Decorator,裝飾器

如果你用了 displaytag 一段時間後,就會發現,他真的很好用!不過,有時候看起來彈性好像不夠。例如我希望某一個 column 的資料能夠有個 hyper link 到某一頁面,不過我需要傳送的 URL 中需要包含超過一個以上的 parameters,那我該怎麼辦?因為 displaytag column tag 只有提供一組 parameter 可以讓我們指定傳送的資料。

這問題的解法有很多種,目前我是使用過兩種。

第一種方法就是指定 displaytag table tag 中的 uid attribute,該 attribute 是用來讓我們 assign a variable 對於每一行的 row data,那這樣又怎樣勒?這樣就可以讓我們在外部使用 EL 的方式來取得該 row data 中的所有 column data 囉!到這裡應該可以瞭解吧!總而言之,如果我們指定 uid = "obj",那我們就可以在我們的程式中這樣用:

<display:table uid="obj" ...>

    <display:column ...>

        <a href="/Test.do?a=${obj.a}&b=${obj.b}" >link</a>

    </display:column>

</display:table>

第二種方法就是使用 displaytag 所提供的 decorator,我們可以建立一個 class extends TableDecorator,這樣我們就可以在 displaytag 的 table tag 中指定 decorator attribute 到我們剛剛建立的 class。

並且我們在此 class 中撰寫一個 method,此 method 需要遵守 JavaBean 的命名規則。

public String getLink() {

    MyObj obj = (MyObj) super.getCurrentRowObject();

    return "<a href=\"/Test.do?a="+obj.getA()+"&b="+obj.getB()+"\">";

}

上面程式中的第一行中,假設我們傳送給 displaytag 的物件是一個由 MyObj 物件所組成的 List 物件,所以每次 displaytag 在執行到 column tag 時,會迭代此 List 物件,並取出 MyObj 物件,所以我們的 getLink() method 可以藉由呼叫 super.getCurrentRowObject() method 來取得目前正迭代到的 MyObj 物件。因為 super.getCurrentRowObject() method 是回傳 Object 物件,所以我們需要作一次的 Casting。接著有了目前正迭代的物件,我們就可以在程式中拼湊出我們所需要的連接網址,也就是範例中的 anchor tag,當然,如果您想得更快,您也可以在這裡拼湊出一段 javascript 傳送到前端。

後端的程式完成後,我們要如何讓此 method 與前端接軌呢?除了指定 displaytag 的 table tag 中的 decorator attribute 外,我們還需要對相對應的 column 指定他的 property 為 link,也就是如下:

<display:column property="link" ... />

超簡單的!這樣的方式可以很輕易的 reuse,也就是在其他 displaytag 的頁面中我們可以指定某個 column 的 property 為 link,就可以為該頁面提供一個同樣的連結。

不過,如果你只是想要純粹將該 column 的 value 作一個顯示上不一樣的樣式話,例如數字要顯示出千分位符號。那你就不需要使用 table decorator,也就是你不需要使用方才我們所建立的 class,你可以改用 DisplayColumnDecorator,這不是一個 class 而是一個 interface,所以我們要改建立一個 class implements DisplayColumnDecorator。要產生這樣的 class,我們當然需要 implement 一個

decorate(java.lang.Object columnValue, javax.servlet.jsp.PageContext pageContext, MediaTypeEnum media)

method,因為這是官方的 API 中的規定。由此 method 的 parameters 中我們可以發現,我們可以直接取得此 column 在前端中指定的 value,而且我們可以取得 JSP 中 page scope 的物件,也就是我們可以在前端將資料儲存在 page scope,到了此 column decorator 我們就可以再次取回某些儲存在 page scope 中的資料。另外的 media 是可以得知 export 的型態。

所以由 parameter 我們可以著手進行我們的包裝作業了。我們在 decorate method 中將 columnValue 先轉換成 int 在透過 DecimalFormat class 進行數字的千分位符號加入:

DecimalFormat df = new DecimalFormat("###,###");
return df.format((int) columnValue);

同樣的,後端的程式完成後,前端我們就在我們需要進行包裝的 column 中指定 decorator attribute 到剛剛的 class 就可以了!

同樣的,使用 decorator 可以達到 reuse 的效果,讓系統在維護上更加的方便。好方法與大家分享之~

2008年10月8日 星期三

[Displaytag] 數字排序,外部排序

Displaytag 的確可以讓我們省下分頁功能的撰寫,並且透過輕鬆簡單的設定就可以完成。

不過,當你面對到數字的排序時,你就會發現頭大了!為甚麼 850 會比 1001 大?這下問題嚴重了,有經驗的人一看就知道這是因為 displaytag 排序的方式是由左至右比較編碼(我也不知道是用 ASCII 還是哪種方式),總之,排序出來的結果就不是我們要的!

首先我們需要先建立一個新的 class 他主要是用來比較出真正的數值,而不是用編碼的方式來比較,那為什麼要比較勒?很簡單!因為所有的排序都是兩兩比較的阿!既然需要比較,那我們就應該要 implement Comparator,而且,在JDK 5.0 以後的環境,我們有了 Generic 可以使用,所以我們就設定我們要 implement Comparator<String>。那又為什麼是要用 String 呢?因為當初會排序失敗,是因為 displaytag 是直接用字串來比較排序的!所以就用 String 囉~ 很直觀吧!

我們完成的 class 如下:

public class NumberComparator implements Comparator<String>
{

    public int compare(String arg0, String arg1)
    {
        Double num1 = Double.parseDouble(arg0);
        Double num2 = Double.parseDouble(arg1);
        return num1.compareTo(num2);
    }

}

在這裡我們因為 implement Comparator interface,所以我們要實做出 compare() method,那因為我們是要作數字的比較,我們就將傳入的兩個字串parameter 先轉換成 Double (這裡不用 Integer 是怕數字的位數不夠,乾脆就一勞永逸)。

Then? 看到這裡你一定會這樣問,好像很直觀的想法到這裡就卡住了!

別急,我們還沒找過 displaytag 的 tags 與 attributes。看看 tag reference 我們就可以發現,display:column tag 中有個 attribute 為 comparator,並且從描述中就可以瞭解到此 attribute 是用來讓我們可以增加自己的 comparator,所以我們就將我們剛剛傳寫好的 NumberComparator 指定給要使用數字排序的 column。這樣就大功告成~ 收工!~

2008年5月3日 星期六

[Displaytag] Displaytag:一個可以簡單完成分頁的 library

對於 web programmer 來說,分頁的資料顯示方式確實是一項令人頭痛的問題,因為必須先支到資料內容的長度,資料分頁後的頁數,以及使用者目前瀏覽到哪一個分頁的資料等等

Displaytag

是一個可以讓 web programmer 輕鬆就完成多資料分頁功能的 open source solution

官方網站中對於自家的 library 有了一些很詳盡的說明,以下就對於分頁的功能作部分的探討

Displaytag 首先就如同其他 JSP tag 一樣,需要再使用前先宣告

<%@ taglib uri="http://displaytag.sf.net" prefix="display" %>

接著我們就是以 display 做為 Displaytag 的 namespace

簡單的分頁使用就是對 table 設定 pagesize attribute:

<display:table name="myList" pagesize="10">
</display:table>


接著就是如果我們的 framework 使用的 Struts,我們還可以讓每次換頁時,都向 controller 重新取資料,那我們就必須設定 requestURI attribute:
<display:table name="myList" pagesize="10" requestURI="/paging.do">
</display:table>


這樣就會在每次分頁時,重新跟 paging controller 要求資料

不過你一定會很好奇,那他要的資料是要部分呢? 還是全部的資料?

別以為程式會這麼聰明,每次分頁時,當然要的都是全部的資料啊!!但是它只會顯示你要看的中間幾筆資料!!

那這樣記憶體空間不就浪費了!! 沒錯~~

所以在 displaytag 中提供了兩種方式來只取部分資料

不過我只有是成功其中一種 XD

首先建立一個 class 是 implement org.displaytag.pagination.PaginatedList:
public class MyPagingList implement org.displaytag.pagination.PaginatedList {
}

裡面會有一些需要實作出來的 methods:
getFullListSize():告知前端的 displaytag 總共會有多少筆的資料,讓前端可以產生所需要的頁數

getList():讓前端的 displaytag 取資料的 method,List 裡面存放的是取出部分的資料

getObjectsPerPage():告知前端的 displaytag 一頁顯示的資料筆數

getPageNumber():告知前端的 displaytag 目前是屬於第幾頁的資料

getSearchId():讓前端的 display 用於搜尋的功能

getSortCriterion():告知前端的 displaytag 目前排序的依據

getSortDirection():告知前端的 displaytag 目前排序的方向 ASC or DESC

我們可以利用後端的 Model 來取得這些資訊,並將這些資訊給與上面我們所建立的物件,再透過 controller 來給與 view 端的 displaytag 作顯示

對於使用者來說,觀看的內容並沒有任何的差別,但是對於顯示的速度來說確實是會加快(當總資料的筆數有上萬筆來說),因為取部分資料的方式,對於記憶體來說,永遠都會只占有那幾筆的數量,在空間上來說確實是一項節省