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

2015年7月28日 星期二

Composite Component 介紹

Composite Component 介紹
hycyeh39@gmail.com

實作步驟:

1. 在 [context_root/resources 下建立 your_directory/your_component.xhtml。(後續使用時的 name space 為 http://java.sun.com/jsf/composite/[your_directory],tag 的名稱為 your_component。

2. 在 your_component.xhtml 中建立複合元件所需要的: 1) interface 及 2) implementation. Your_component.xhtml 中仍要有 及 。

3. 使用 來宣告複合元件要使用的: attributes, attachable converters, attachable validators。

3(a) 宣告屬性的名字及是否為必要。 的使用參考http://docs.oracle.com/javaee/6/tutorial/doc/gkhwv.html。

3(b) 宣要可附加 converter 及 validator 的 UI元件。target 屬性指向在 中要套用的 UI 元件的名稱;name 為在使用複合元件時converter/validator 標籤要附加時的接口介面。

3(c) 另外還可使用 開放 中的元件,讓使用者附加 converter 至該元件。

3(d) 使用 開放 中的元件,該使用者能附加 actionListener 至該元件。

4. 使用 實作複合元件。在 可以放入多個 UIComponent 元件進行實作。

4(a) 要在元件中使用屬性的值,使用#{cc.attr}。#{cc} 是複合元件中的自動變數(implicit variable),先前在 中定義的屬性可以透過 #{cc.attr.yourAttributeName}取得。

範例說明




 一個 composite component,內有 email 輸入欄位及一個圖示的連結。按下圖示的連結後會驗證 email 格式是否正確,並執行連結指定的方法。

檔案結構


檔案結構如上圖。在[context_root]/resources 下放置複合元件的 xhtml 檔comps/emailBox.xhtml、使用到的 css 檔css/styles.css及圖片檔 images/sendmail.jpg。

Composite Component 定義檔 ([context_root]/resources/comps/emaiBox.html)


  • #9 宣告複合元件所需的輸入屬性
  • #10 第一個屬性為 email,是必要的屬性
  • #11 第二個屬性為 image,為圖示連結所要使用到的圖形
  • #12 第三個屬性為 actionMethod,為按下圖示連結後要執行的方法。方法的簽章如 method-signature 屬性中指定,傳回 String,沒有傳入參數。
  • #14 公開位於 #19 的 Validator 及 Converter 介面
  • #17 Composite Component 實作開始
  • #18 宣告一個及 id 為 form
  • #19 宣告一個文字輸入元件  此元件的 value 屬性會 bind 到使用者透過屬性  email 所傳入的 bean property。
  • #20 宣告一個 元件,其 action 為使用傳入之方法,方法簽章在#13 已宣告。
  • #21 宣告一個 元件,做為 元件的圖示。該元件的 url 屬性也是由使用者傳入,並透過屬性 image 取得。


使用自訂複合元件


上圖左邊為使用前述複合元件的 index.xhtml 檔 。在 index.xhtml 中:
  • #7 宣告要引用的複合元件的 name space 為 http://java.sun.com/jsf/composite/comps。注意,url 中的 comps 目錄的實際位置是 [context_root]/resources/comps
  • #11 使用自訂複合元件,該元件有 3 個屬性: email, image, actionMethod。這三個屬性於上圖右手邊的 emailBox.xhtml 檔案中的 #10~#13 中定義。
  • #14 為複合元件中的 的外掛進來的驗證器,使用 for 屬性指定該驗證器要對應的介面接口,該接口位於複合元件介面中的  


參考資料
  1.  EE 6: Develop Web Applications with JSF, Student Guide II, 2014
  2. 來源: Composite Example in lab files for Java EE 6 Training course.
  3. Chapter 12 Composite Components: Advanced Topics and Example, https://docs.oracle.com/javaee/6/tutorial/doc/gkhxa.html, accessed on 7/29/2015.