Custom Component 簡介
hychen39@gmail.com
Steps to create a custom component:
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
。
若要元件本身要負責 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.
在 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()
執行上述的動作。這個動作是發生在 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
。在
JSF 2.1
版中此參數好像尚未被使用。
結束輸出使用 responseWriter.endElement(String name)