2009年7月29日 星期三

[Struts2] Collection-based UI Tags

除了之前比較簡單的 UI tags 之外,Struts2 framework 當然也會提供一些比較複雜的 tags,不然就無法符合使用者的需求了!在這裡我們將會介紹三種以 Collection 為基礎的 UI tags:1) select tag、2) radio tag 與 3) checkbox list tag。

這裡所謂的 Collection-based 就是在 Java 端的 property 是以 Collection 為主,如 List、Map 等,而這些 property 要如何在頁面上顯示,就是這裡的重點了!同樣的,在這裡我只會介紹每個 tag 特殊的 attributes,而共用的 attributes 就請參考 Simple UI Tags 的共同 attributes。

1) Select Tag
Select Tag 對於 Collection-based UI Tags 來說,應該是最常使用到的 tag!因為下拉式選單是最常見到的顯示方式,下面是最簡單的範例:
<s:select name="user.name" list="{'Silver','Brian','Mike'}" />

我們使用到 OGNL 的 List 型態,並且由 OGNL 幫我們快速建立。name attribute 就是該選單選到的值要被儲存到的目標 property,而 list 則是我們要顯示在頁面的 Collection-based 物件。以下就是 select tag 的 attributes:
















































AttributeTypeDescription
listCollection-based type, List, Map, Array or Iterator用來產生 select tag 的 option list
listKeyString根據 list attribute 中,指定 property 用來產生 option tag 的 key
listValueString根據 list attribute 中,指定 property 用來產生 option tag 的 value
headerKeyString類似於 listKey,產生在 Collection 之前
headerValueString類似於 listValue,產生在 Collection 之前
emptyOptionBoolean指定是否自動產生空的 option 在 header 之前
multipleBoolean是否可以多重選擇
sizeInteger指定一次顯示的筆數,會有 scroll bar 輔助多餘的 option


上述的 attributes 中,比較需要注意的是 list, listKey 與 listValue 這三個,list 是用來設定我們要顯示的 Collection-based property,在這裡要注意的是 list attribute 的型態只能接受 Collection, Array, List, Map 與 Iterator,如果型態不對就會出現錯誤!另外,listKey 則是根據 list 中所存放物件的某個 property,例如:
private List<UserBean> userList;

userList 中存放了一堆的 UserBean 物件,每個 UserBean 物件都有 name 與 age 的 property,那我們的 select tag 撰寫成:
<s:select list="userList" listKey="age" listValue="name" />

就是指定每一個 select 之下的 option 都會以 UserBean 物件中的 age 作為 key (也就是作為 option tag 中的 value attribute),而 name property 則為 option 顯示的內容。也就會變成如下:
<select>

<option value="10">Silver</option>
<option value="24">Su</option>
<option value="50">Jason</option>

</select>

從上面的結果我們也可以知道,我們的 userList 中存有三個 UserBean 物件。

而比較特別的是 headerKey 與 headerValue 這兩個 attributes,就目前為止我也搞不清楚這兩個 attributes 有什麼特別的用處,如果我們指定的 list 物件中需要增加一項資料,但我們不想動到 list 物件,那我們可以使用 headerKey 與 headerValue 來增加一項 option。而 emptyOption attribute 則是指定我們是否需要 Struts2 framework 幫我們自動產生一個空白的 option。size attribute 則是用來指定一次顯示的數量,如果我們不指定就會是一次顯示一項,這樣會比較像是下拉式選單,如果顯示數量大於 1 又小於 list 的總數量,則會多了上下的 scroll bar 幫助我們選擇。

這幾個 attribute 中比較有趣的是 multiple,以往的下拉式選單都是多選一,如果我們指定 multiply="true",選單就可以呈現多選,在不指定 size attribute 之下,多選的選單就會將整個 list 展開。如果我們採用多選的選單,選單送出的 property (也就是 name attribute 所只到的 property) 必須是一個 Collection-based property,這點應該很直觀,既然可以多選,就表示我們需要用 Collection-based 物件來接收,否則就失去多選的意義了!

2) Radio Tag
使用 radio tag 就如同使用 select tag 一樣,只是少了許多 attributes 可以使用罷了:























AttributeTypeDescription
listCollection-based type, List, Map, Array or Iterator用來產生 select tag 的 option list
listKeyString根據 list attribute 中,指定 property 用來產生 radio 的 key
listValueString根據 list attribute 中,指定 property 用來產生 radio 的 value


由於使用的方式大同小異,所以我就不舉例了!而要注意的是,radio tag 一定是單選,所以 name attribute 指到的 property 可以是 primitive type 或 Collection-based type。

3) Checkbox List Tag
使用 checkboxlist tag 跟 select 也是類似,只是要注意的是,不要將 checkbox 與 checkboxlist 搞混了!checkbox 是單選,checkboxlist 是多選!而 checkboxlist 提供的 attributes 有:























AttributeTypeDescription
listCollection-based type, List, Map, Array or Iterator用來產生 select tag 的 option list
listKeyString根據 list attribute 中,指定 property 用來產生 checkbox 的 key
listValueString根據 list attribute 中,指定 property 用來產生 checkbox 的 value


不過在這裡要注意的是,因為 checkboxlist 一定是多選,所以 name attribute 指到的 property 一定是要 Collection-based type。

最後我想探討的是如何將 list 的選項初始化,由於 name attribute 是指定該元件在表單送出後的目的地,如果我們要讓某個 select tag 中的 option 被預先選起來,我們就要在相對應的 property 事先給予值,例如我們有一個 select tag 如下:
<s:select name="myFavorite" list="favoriteList"
listKey="key" listValue="value" />
這個 select tag 的目的地是 myFavorite property,如果我們希望 list 中某個 option 先被選起來,我們就將 favoriteList 物件中的該項目的 key 填入 myFavorite property,這樣 Struts2 framework 就會幫我們選起該 option 了!總之記得,資料怎麼來就怎麼去!

2009年7月16日 星期四

[Struts2] Simple UI Tags

之前我們介紹了一些 tags,主要都是輔助 programmers 完成頁面上資料的存取,在這裡我將開始描述關於顯示在頁面上與使用者互動的表單 (form)。不過,我先簡介一些共同有的 attributes,因為這些 attributes 如果在介紹 tag 時重複敘述,感覺過於累贅!

共同 attributes

共同的 attributes 就是所有的 tags 都會擁有的,為了之後將重點放在各個 tags 上,我們就將這些 attributes 分開來介紹,這裡要注意的是,如果某個 attribute 的 type 屬於 String,那就表示該 attribute 的 value 只能接受字串,如果想要將 OGNL expression language 寫入,就必須在 expression 加上 %{} 符號 - %{ expression } - 這樣 Struts2 framework 就可以正確解析 OGNL 了!如果該 type 屬於 Object,那你就可以大方的將 OGNL expression 置入,無須加上 %{} 符號了!

































































AttributeTypeDescription
nameString用來設定 form 元件,並且會依照 name 所設定的值,將其對應到 Action 中的 property
valueObject用來設定 form 元件中所包含的值
keyStringi18n 用
labelString設定某個 form 元件顯示的字串
labelpositionString設定 label 顯示的位置:left 或 top
requiredBoolean如果設定為 true,則該 form 元件的 label 會出現 * 符號
idString相同於 HTML 的 id 屬性,通常用於 java script 與 CSS
cssClassString相同於 HTML 的 css 屬性
cssStyleString相同於 HTML 的 style 屬性
disabledBoolean相同於 HTML 的 disabled 屬性
tabindexString相同於 HTML 的 tabindex 屬性


Simple UI tags


上面描述的是共同的 attribute,以下將介紹一些 UI 相關並基礎的 tags,對於共同的 attribute 就不會重複提及。

1) Head Tag
對於任何的 HTML 或 JSP 來說,要產生一個網頁通常都是以 <head> 開頭,對於 Struts2 提供的 <head> 來說,卻不同於一般的 HTML head tag,如果你試過 Struts2 提供的 head tag,你應該會發現,tag 本身並不作任何事情,而是在網頁中默默的幫我們加入一些檔案連結,這些檔案的連結目的在於支援其他的 tag 完成設定。在使用 Struts2 提供的 head tag 時,一定要放置於 HTML 的 head tag 之下,否則自動產生的檔案連結會放到不正確的位置而導致無法使用某些功能!
Struts 的 head tag 本身並沒有其他的 attribute 需要設定,我們可以簡單的使用:

<s:head />

這樣的撰寫就會自動幫我們產生一些檔案的連結。

2) Form Tag
Form tag 對於 UI 來說是最重要的 tag,因為他是所有 UI tag 的上層元件,如果對於 HTML 熟悉的話,form tag 的使用應該不會陌生,而且我們之前就看過蠻多的範例了!下表是一些 form tag 的 attributes (除了共同 attributes 之外):









































AttributeTypeDescription
actionString指定該 form 送出的目標 Action
namespaceString目標 Action 的 namespace,預設值是目前的 namespace
methodString相同於 HTML 的 method attribute,預設值是 POST
targetString相同於 HTML 的 target attribute
enctypeString若要使用檔案上傳,則填入 multipart/form-data
validateBoolean設定該 form 是否使用 javascript 驗證,當使用 Validation Framework 時,此 attribute 要設定為 true,驗證功能才能正常運作

在上述眾多 attributes 中,最重要的莫過於 action 了!因為他主導整個 form 的目標 action,在這裡設定 action attribute 時要注意,我們無須填入 .action 的縮寫,因為 form 元件會幫我們完成,如果目標的 action 與現在是相同的 namespace,我們就無須填寫 namespace,否則我們就需要 namespace attribute 幫助我們找到 Action。每當 form 元件被執行時會有以下三種可能發生的狀況:
  1. 如果 action attribute 沒有被設定,則 form 的目標就會指向目前的 Action
  2. 如果有指定 action attribute,form 就會根據 Action 的位置配合上 namespace 來設定目標。如果 namespace 沒有被指定,就會使用目前的 namespace。當我們在設定 action attribute 時,我們無須加上 .action 的延伸網址
  3. 如果 action attribute 所指定的值不屬於一個 Action,form 元件的目標就會直接將該值作為目標。這種情況下,我們就需要自己加上延伸網址 (如:.jsp, .html 等),並且我們的相對位址需要以 / 作為開頭,例如:/myApp/b.jsp。另外要注意的是,如果我們採用此種方法,即使我們指定 namespace attribute,form 元件也會自動忽略!
3) Textfield Tag
這個 tag 對於你來說也是無可避免的!因為沒有此 tag,你就無法取得使用者的資料,所以這個 tag 也算是最常使用的!不過要注意的是 textfield tag 的 name 與 value attribute,一般來說,我們無需要指定 value attribute,因為我們只需要指定 name attribute 讓 OGNL 幫我們去 ActionContext 中取得對應的值來填入,當然,我們也可以填寫 value attribute,但是要注意的是,如果我們自己有填寫 value attribute,那 name 指定的 OGNL 所取到的值就不會顯示在 textfield tag 上了,這點要注意喔!以下是一些 textfield tag 的 attributes:


























AttributeTypeDescription
maxlengthString指定 textfield 的可輸入最大長度
readonlyBoolean如果設定為 true,則該 textfield 就無法輸入字串
sizeStringtextfield 的長度

4) Password Tag
這個 tag 跟 textfield 幾乎是一樣的,不過當使用者輸入字串時,所有的文字都會被包裝成其他的符號,以防資料外洩!這個 tag 的使用其實跟 textfield 是相同的,所以我就不多說了!以下是 password tag 的 attributes:































AttributeTypeDescription
maxlengthString指定 textfield 的可輸入最大長度
readonlyBoolean如果設定為 true,則該 textfield 就無法輸入字串
sizeStringtextfield 的長度
showPasswordBololean使否將 OGNL 取到的值顯示在 value 中,預設 false

上面列出的 attributes 中,幾乎跟 textfield 是一樣的,不過多了一個 showPassword attribute,這個 attribute 是讓我們設定是否要將 name attribute 中指定的 OGNL 取到的值顯示,在這裡我所謂的顯示不是大剌剌的用文字方式顯示,當然是有被遮蔽的顯示,不過從原始碼中還是可以看到原來的值,所以 showPassword attribute 預設值為 false,為的就是不要產生一些 security issues 來困擾 programmers,在這裡我也建議你不要將此 attribute 設定為 true!

5) Textarea Tag
同樣的,textarea 從開發角度上來說,與 textfield 並沒有太大的差別,主要就是提供使用者多行的輸入欄位!以下就是一些 textarea tag 的 attributes:































AttributeTypeDescription
colsInteger指定 textarea 的行數
rowsInteger指定 textarea 的列數
readonlyBoolean如果設定為 true,則該 textarea 就無法輸入字串
wrapString相同於 HTML 的 wrap attribute

6) Checkbox Tag
這裡的 checkbox 元件如果你認為跟 HTML 的 checkbox 一樣的話,那你就錯了!因為在 Struts2 提供的 checkbox 元件是一個單一 HTML 的 checkbox,這是什麼意思呢?一般的 HTML checkbox 提供了多個選項讓使用者選擇零或多個,但是這裡的 checkbox 只提供使用者一個選項,也就是選或不選。所以這個 checkbox 不同於 HTML 的 checkbox。這個 checkbox 只提供 Boolean 的功能,也就是說其對應的 property 一定要是一個 boolean property,如果要像 HTML 提供的 checkbox 有相同功能的話,要改使用 checkboxlist tag,現在不會提到!
以下是 checkbox tag 的 attribute:





















AttributeTypeDescription
fieldValueString這裡所填寫的 value 是真正會被傳送到 Action 中的值,主要是 true 或 false,預設值為 true
valueString這裡是用來判斷此 checkbox 是否會被勾選

看到上面的 attribute 你一定會感到很奇怪,value attribute 已經是共同的 attribute 了,為何在這裡還會在重新點出呢?原因就在於 checkbox 元件還有一個 fieldValue 這會與 value attribute 搞混!所以在這裡要特別點名。
fieldValue attribute 中的值代表當此 form 被送出後,fieldValue 中填寫的值會真正被送到 Action 中對應的 property,而 value attribute 只是用來判斷此 checkbox 是否需要被勾選。當 OGNL 取得 ValueStack 中的值,會將此值寫入 value attribute 中 (任何一個元件都是這樣),使用者可以看到此 checkbox 是否有被選起來。所以 fieldValue 與 value attribute 之間不要弄混了!
另外,checkbox 所指定的 name attribute,其對應到的 property 需要是一個 boolean 型態,否則會無法運作!

在這裡列出了六種簡單的 UI tag 給你參考,這裡列出的都是針對單一的 property,之後我將介紹關於 Collection-based 相關的 UI tags。

2009年7月8日 星期三

[Struts2] 探討 OGNL 運算功能

關於 OGNL (Object-Graph Navigation Language),我們之前已經提過相當的多了!不過那些都只是在 navigate 的部份,也就是如何將資料在 String-based HTTP 與 Java-type 之間做轉換與存取。熟悉 JSP 的 Expression Language (EL) 的 programmers 來說,EL 不僅僅提供了存取資料的功能,也提供好用的運算功能!身為比 EL 更強大的 OGNL 來說,這也是基本的配備!

OGNL and Collection

之前我們有提過 OGNL 如何在 Collection 型態的物件進行 navigate,但不只這樣喔~對於 Collection 型態的物件,OGNL 提供了一些好用的功能:1) 取得資訊、2) 快速建立 與 3) Filter and Project。

1. 取得資訊
如果是要取得 Collection 中的資料,我在內建 OGNL Type Converter 中就有提過,不過我在這裡想要說得是,如果我們想要知道 Collection 中的大小,那我們是不是要將 Collection 的大小先儲存到某個 property 中?答案當然是不用!OGNL 怎麼不會提供這樣的功能勒~不僅僅是 Collection 大小,就連判斷是否為 empty 都有支援。
如果我們要取得 Array 的大小,我們就要使用 array.length;如果是 list,則是 list.size;而 map 的話就與 list 是相同的。而 empty 的判斷只有 list 和 map 有提供,使用都是 list.isEmpty。

2. 快速建立
在 JSP 頁面中所使用的 Collection 型態物件,不見得都要屬於 Action 的 property,OGNL 提供了快速建立 Collection 物件的機制,讓 programmers 可以容易的建立 list 或 map,這對於在頁面上提供一個 select 下拉式選單來說是很方便的。不過目前我不想提到關於 select 選單的部份,主要先考慮如何快速建立 list 或 map,對於下列的 Java code 你應該不陌生:
public List<String> createList() {
List<String> list = new ArrayList<String>();
list.add("Hello");
list.add("Silver");
list.add("Welcome");
return list;
}
這段程式碼主要就是建立一個 list 並且容納我們想要的字串物件,對於 OGNL 來說,要建立一個一樣的 list,簡直是易如反掌:

{"Hello", "Silver", "Welcome"}

別懷疑自己的眼睛,沒錯!就是這麼簡單~我們只要短短的 OGNL 就可以幫我們實現出一串的 Java code。現在的你是否也想要知道 map 的建立呢?別急,我們先看看一段程式碼:
public Map<Integer, String> createMap() {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1,"Hello");
map.put(3,"Silver");
map.put(100,"Welcome");
return map;
}
這同樣也是你很熟悉的程式碼,我想我就直接告訴你這樣的 OGNL 會是:
#{1: "Hello", 3: "Silver", 100:"Welcome"}

看到這裡,連我自己都不禁懷疑,到底 OGNL 在哪裡需要 # 符號,在哪裡又不需要呢?目前我還看不透,不過自己在使用時就記住,list 不用 # 而 map 需要~

3. Filter and Project
OGNL 除了上面兩種功能外,另外還提供了過濾 (filter) 的功能與投影 (Project),這看起來匪夷所思,實際上卻是很好用的東西。Filter 就是 OGNL 提供讓 programmers 針對 Collection 撰寫 rule 來取出我們想要的東西;Project 就是將某個 Collection 指取出部份的資料並投影到另一個 Collection 之中。這兩個東西乍看之下好像很類似,其實不然,直接舉個例子應該會比較清楚。假設我們有一個 User 物件:
public class User {
private String name;
private int age;
//getter and setter methods
}
然後,我們有一個 List 儲存一堆的 user 為 users,這次我們先展示 filter 功能,如果我們將 OGNL 撰寫成:
users.{? #this.age > 18}

則 OGNL 會幫我們將 users 中的所有 User 物件的 age 進行過濾,並且依照我們所指是的 rule 完成,而 rule 中的 #this 代表每一次 OGNL 迭代所指向的目標物件 (類似於 iterator.next())。
接下來是 project 的部份,同樣的我們使用 users 來示範,如果我們的 OGNL 為:
users.{name+' '+age}

則 OGNL 會幫我們將 users 中的所有 User 物件的 name 與 age 取出,並用一個空白字串組成新的字串,在存成一個新的 list 之中。當然,我們可以結合 filter 與 project:

users.{? #this.age > 18}.{name+' '+age}

這個結果就是上面兩個 OGNL 的結合。不知道你是否有注意到一個規則,當我們在使用 filter 時,我們的撰寫方式為:

collectionName.{? expression }

反之,當我們使用 project 時,我們的 OGNL 撰寫為:

collectionName.{ expression }

也就是 filter 的 expression 之前有加 ? 符號,而 project 沒有。

Other Functions

OGNL 不僅提供上述的功能,另外提供對於運算方面的功能以及一些特殊的功能:1) 運算功能、2) 呼叫 method 與 3) 存取 static method 與 field。

1) 運算功能
OGNL 提供了多種型態的變數讓 programmers 可以快速的宣告:
  • char - 'a'
  • String - "a" 或 'silver'
  • boolean - True, False
  • int - 123
  • BigDecimal - 123b
  • BigInteger - 123h
另外就是關於運算的部份,就如同 Expression Language(EL) 一樣,OGNL 提供了許多的運算:
  • add: 1+2, "hello"+"silver"
  • subtract: 2-1
  • multiply: 2*1
  • divide: 6/3
  • modules: 5%2
  • increment: a++, ++a
  • decrement: a--, --a
  • equality: a==b
  • less than: a<b
  • greater than: a>b
2) 呼叫 method
對於熟悉 EL 的 programmer 來說,最希望的一件事就是 EL 能夠呼叫 method,不過很可惜的,目前為止,EL 還無法做到~不過別氣餒,OGNL 身為 EL 的強化版,對於 programmer 多年的渴望終於有了回應,OGNL 提供 programmers 可以呼叫 method 的功能,而且使用上相當的簡單。假設我們的 User class 有一個 method 為 toString(),如果我們在 OGNL 想要呼要的話,只要簡單的撰寫成:

user.toString()

沒錯!就是這麼簡單,就如同我們在 Java 中撰寫呼叫 method 一樣~

3) 存取 static method 與 field
OGNL 除了針對 instance 的 field 與 method 做呼叫外,對於 static 的 field 與 method 也是可以呼叫的:

@fullClassName@[field 或 method]

不過要注意的是,我們在 struts.xml 檔案中需要在設定一個 constant:
<constant name="struts.ognl.allowStaticMethodAccess"
value="true" />

這樣我們才可以開始使用 OGNL 呼叫 static method。假設我們在 User class 中有一個 static int age,我們在 OGNL 中就可以這樣呼叫:

@silver8250.bean.User@age

要注意的部份只有 static 呼叫是要用 @ 符號來呼叫,這點記住就好了!其他的呼叫都是一樣的,當然我們可以取得某個 static 物件後,再採用原來的呼叫方式取得更進一步的 field 等:

@silver8250.bean.User@nextUser.name

NOTE:
請盡量保持 JSP 頁面的簡單,雖然 OGNL 擁有運算的能力,不過我還是建議你將這些運算移到 action 中執行,畢竟 JSP 頁面屬於 View,他只負責將 data 顯示給使用者,我們不應該將運算的動作在這裡執行,這樣會破壞 MVC 原先美好的本意!除非有必要這樣做,否則請饒了日後 maintain 的人吧~