JSFのカスタムコンポーネント作成(2)

前回はカスタムコンポーネントを作成し、そのコンポーネントJSFタグに登録しました。今回は、そのコンポーネントを改良して描画部分を分離する方法を紹介します。

前回作成したカスタムコンポーネントは、コンポーネント内で描画も行っていましたが、一般的には描画処理を分離するほうがいいかと思います。

描画部分を分離するにはjavax.faces.render.Rendererクラスを継承し、以下のメソッドをそれぞれ必要に応じてOverrideするだけです。


encodeBegin
encodeChildren
encodeEnd
decode

では、前回作成したHogeコンポーネントを改良し、描画処理の分離とします。

基本的には上記メソッドをRendererクラスを継承したクラスに移動するだけです。

@FacesRenderer(componentFamily="javax.faces.Input",
        rendererType="org.ui.Hoge")
public class HogeRenderer extends Renderer {
    public void encodeBegin(FacesContext context, UIComponent hoge) {
        ResponseWriter writer = context.getResponseWriter();
        ...
        String clientId = hoge.getClientId(context);
        ...
        writer.startElement(...);
        ...
    }

    public void decode(FacesContext context, UIComponent hoge) {
        Map<String, String> requestMap = context.getExternalContext().getRequestParameterMap();
        ...
        String value = requestMap.get(hoge.getClientId(context));
        ...
    }
}

まず、@FacesRendererアノテーションを利用して、componentFamilyとrendererTypeを明記する必要があります。

前回作成したUIHogeクラスのencodeBeginとdecodeメソッドを移動させただけですが、注意点として、各メソッドの引数にUIComponent型で、対象のカスタムコンポーネントを受け取るようになります。

componentFamilyというのはいわゆる、コンポーネントの種別であり、例えばInput系(inputText、inputSecretなど)のコンポーネントであれば上記のようにjavax.faces.Inputと指定します。

あとは、my.tag.xmlに作成したRendererを登録するだけです。

<facelet-taglib version="2.0"
    xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
    http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd">

    <namespace>http://hogeapp.ui.org/my-taglib</namespace>
    <tag>
        <tag-name>hoge</tag-name>
        <component>
            <component-type>org.ui.Hoge</component-type>
            <renderer-type>org.ui.Hoge</renderer-type>
        </component>
    </tag>
</facelet-taglib>

org.ui.Hogeで作成したRendererクラスを登録します。

実際にカスタムコンポーネントを作成してタグライブラリとして組み込む場合は、描画処理をRednererクラスを継承した描画専用クラスで行うほうが一般的かと思います。