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>
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
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 元素中的屬性值
結束輸出使用 responseWriter.endElement(String name)
1 則留言:
schema 的 url 有誤, 正確請參考:
http://javaevangelist.blogspot.tw/2012/07/jsf-tip-of-day-schema-declaration-issue.html
張貼留言