2015年8月19日 星期三

JSF 選擇式元件的使用

JSF 選擇式元件的使用

2015/08/20

簡介

介紹  h:selectManyListBox,h:selectOneListBox,h:selectOneMenu,f:selectItems
等標籤的使用方式。

使用 產生不同形式的網頁中選擇式元件,如 List Box、Check Box List、Radio List及Menu,參考下圖[1]:


在 的 body 中使用 f:selectItem 或者 f:selectItems 帶入可選擇的清單項目。

f:selectItem 的使用上,使用該標籤的 itemLabel 及 itemValue 屬性來設定選單項目的標籤及標籤值 ,使用範例可參考[2]。

相較於 f:selectItem 需手動列舉項目清單,使用 f:selectItems 可以自動地將 Collection 或者 Arrary 轉成清單項目。Collection 或 Array 中的成員需是 SelectItem 物件或者 Java Object[3]。

以下說明使用 f:selectItems 的方式,一為項目清單使用 SelectItem 物件表示,另一為使用 Java Object 表示。



回傳 SelectItem Collection 產生項目清單

Backing beans

#45-47 的 getOptions() 方法傳回 SelectItem List




JSF Page

#13 中的 的 value 屬性是對應到  cashier backing bean 中的 newsLetters property。

HTML Output



Rendered Results



回傳 Java Object Collection 產生項目清單


Backing Bean

在 Company class 下定義的 Employee 子類別:


在 #41~43 定義的Company 類別的 getEmployee() 傳回 Employee List。

JSF Page

使用 取得項目清單值。 中的 value 屬性指向 backing bean 中傳回 Java Object Array 的屬性; var 屬性為 Array 中的 Object; itemLabel 設為 Object 中做為標籤名稱的 property;itemValue 設為Object 中做為標籤值的 property。


HTML Output



Rendered Result




2015年8月6日 星期四

Custom Component 簡介

Custom Component 簡介

hychen39@gmail.com

Steps to create a custom component[1]:

Step 1: 建立一個javax.faces.component.UIComponent 的子類別。使用 @FacesComponent  annotation 註冊此元件。
Step 2: 若要將元件顯示的工作委由另一個 Render class 處理,則建立一個 javax.faces.render.Renderer 子類別。此為選項步驟。使用 @FacesRenderer annotation 註冊此 Renderer。
Step 3: 建立一個 Tag Library Descriptor,宣告使用客製元件時所使用的custom tag。在TLD 檔中宣告客製元件的 namespace, tag name, component type,  attribute (optional)等。使用 @FacesComponent 註冊元件只能宣告元件的 namespace, tag name, 及 component type,不能宣告元件的屬性,元件的屬性需在 TLD 檔中宣告。在 JSF2.2 以後,若使用 @FacesComponent( createTag=true) 時,若不需要標籤屬性可以不要建立 TLD 檔。TLD 檔名稱結尾需為 taglib.xml,檔案必須位置 [context_root]/WEB-INF下。此外,需在 web.xml 註冊此 TLD 檔,註冊的方式為使用 param
>,如下:
<context-param>
        <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
        <param-value>/WEB-INF/your_tag.taglib.xml</param-value>

param>

Step 4: 在 JSF 頁面中引入客製標籤的 name space 並使用 custom tag.

建立一個javax.faces.component.UIComponent 的子類別。

每個 custom tag 都會對應到一個 UIComponent 的子類別。實務上會建立 javax.faces.component.UIComponentBase 子類別,該子類別已實作 UIComponent class 中的抽象方法。也可以直接建立 UIOutput、UIInput、或者 UICommand 子類別,視元件的用途而定。


覆寫 UIComponentBase 的方法

當建立 UIComponentBase 子類別時,還必須覆寫其 getFamily():String 方法,傳回元件所屬的元件族群(Component Family)。JSF 會利元件所屬的元件族群及 Renderer 的 type 來選擇適合元件使用的 Render[2]。
若要元件本身要負責 Renderer 的工作,則必須負責decode 及encode 兩個階段的工作。在decode 階段需將請求中的參數取出放到元件的 submittedValue  屬性。接著再進行轉換及驗證的動作,成功後才會放到元件中的 value 屬性中(或稱 Local Value)。在 decode 階段需覆寫以下的方法:
·         decode()
在 encode 階段,要將元件中的 value 屬性的取出,並輸出成 HTML 元件。此時需覆寫以下三個方法:
·         encodeBegin()
·         encodeChildren()
·         encodeEnd()

委任 Renderer 工作給 javax.faces.render.Renderer class

若客製元件要將 Render 工作,則需將工作委外給 javax.faces.render.Renderer 類別。Renderer 類別的工作內容就是執行 decode 及 encode 兩個階段的工作。

Renderer class 的建立步驟如下:
1.       建立 javax.faces.render.Renderer子類別並覆寫 decode 及 encode 階段會被呼叫的方法,包括:
·         decode()
·         encodeBegin()
·         encodeChildren()
·         encodeEnd()
2.       在 JSF 中註冊 Renderer
可以使用 @FacesRender annotation 或者在 faces-config.xml [3]中進行註冊

3.       在 UIComponent 子類別中:
·         覆寫 getFamily():String 方法,傳回的字串需和註冊時使用的 標籤內容一致。
·         設定其 rendererType 屬性,其值需和 Step 2 所註冊的 Readerer 的 Renderer Type 一致。使用 UIComponent 的setRenderType() 設定 renderType 屬性。或者直接覆寫元件的 getRenderType():String 使其傳回註冊的 Render Type
完成上述設定後,JSF 會自動由 Render Kit 中找到指定的 Family 下的 render type 類別並自動建立物件實體讓我們使用。



Annotation 和其在 faces-config.xml 中的對應的 XML



Tag Library Descriptor


Decoding 階段會做的事及使用到的類別及方法

取得請求的 client id

facesContext.getExternalContext.getRequestParameterMap() 方法取得存放請求參數的Requests Map。
取得 Request Map 中的參數,  requestMap.get(Object Key) 或者 requestMap.containsKey(Object Key):boolean。

和 UIComponent  相關的操作

取得 client id

uIComponent.getClientID(FacesContext) 取得 UIComponent 的client id (Return a client-side identifier for this component)
例如,下圖中的 UIComponent 的 Client id 為 monthSpinner。


取得UIComponent 中的屬性

取得 UIComponent 中的屬性 uIComponent.getAttributes().get(Object Key)。
uIComponent.getAttributes() 會傳回存放 attributes 的 map 物件。

設定 UIComponent 的 submittedValue特性及 local value

將request 的值存到 UIInput 元件的 submittedValue property,以準備進行 Validation,使用 uIInput.setSubmittedValue() 執行上述的動作[6]。這個動作是發生在 Apply Request Phase。在 Process Validators Phase,若 UIInput 中的 submittedValue 轉換並驗證通過,使用 uIInput.setValue() 將轉換後的值設定到 UIInput 元件的 Local Value 中。

觀察Request Map 中的值如何產生

以下例子說明 Request Map 中的值的產生
當按下 month spinner 的 > 按鈕時,會 submit form。Form 的 id 為 spinnerForm。在spinnerForm 中有2個 input field,其 id 分別為 spinnerForm:monthSpinner 及 spinnerForm:yearSpinner。

參考下圖,當 submit form 時,以下的參數會被提出
1.       #6 的 spinnerForm = “SpinnerForm”
2.       #14 的 spinnerForm:monthSpinner = “1”
3.       #14 的 spinnerForm:monthSpinner.more = “>”
4.       #21 的 spinner:yearSpinner = “2010”

以下是在 Netbeans 中觀察到的 Request Map 中的值。



Encoding 階段會做的事及使用到的類別及方法

取得 Response Writer

需取得 Response Writer 將回應寫到輸出串流傳給 browser。使用 facesContext.getResponseWriter():ResponseWriter 取得 Writer。

取得 UIComponent 中的: client ID,  local values 及 attributes


要取得 UIComponent 的Client ID,使用 uIComponent.getClientID(facesContext):String。
取得 UIComponent 中的 Local value,使用 uIComponent.getValue():Object
取得 UIComponent 中的 attribute,使用 uIComponent.getAttributes.get(Object Key):Object

Render a HTML element

要開使輸出 HTML element,使用 responseWriter.startElement(String name, UIComponent Component):void,其中:
·         Name: 是 HTML Tag 的元素
·         Component 是對應到此元素 UIComponent,該元件中的 pass-through attributes 會被直接寫出到 HTML 元素成為該元素的屬性。
當執行 responseWriter.startElement() 後,便可使用 responseWriter. writeAttribute(String name, Object value, String property) 輸出該元素的屬性,其中:
·         name: HTML 元素中的屬性名稱
·         value: HTML 元素中的屬性值
·         property: UIComponent 中對應到此元素的 property 或者 attribute。若沒有則給 null[7]。在 JSF 2.1 版中此參數好像尚未被使用[8]。
結束輸出使用 responseWriter.endElement(String name)




[3] 使用faces-config.xml 註冊component 及 Renderer 的方式在 JSF 2.2 版本中好像行不通。
[4] E. Burns & C. Schalk, JavaServer Faces: The Complete References, McGraw Hill, 2010, p309
[5] E. Burns & C. Schalk, JavaServer Faces: The Complete References, McGraw Hill, 2010, p319
[6] https://docs.oracle.com/javaee/6/api/javax/faces/component/UIInput.html