6

JSF/Primefaces Web アプリケーションに単純な html5 属性「autofocus」を実装しようとしたときに、コンポーネントがすべての不明な属性を最終的なマークアップに渡さないという事実に注意を促されました。コンポーネントは html マークアップの複雑な組み合わせである可能性があり、属性がまだコンポーネントによって適切に定義されていない場合、属性をどこに配置するかが明確ではないため、この理由は理解できます。

しかし、私にとっての最善の解決策は、オートフォーカスをサポートすることです (および、primefaces が定義していないアプリケーションでサポートしたい可能性のあるその他の属性のタイプ)。

Adding custom attribute (HTML5) support to JSF 2.0 UIInput componentを見てきましたが、これは基本的な JSF コンポーネントに適用されるようで、PrimeFaces コンポーネントには機能しません。

これをサポートするために Primefaces のコンポーネント/レンダリングを拡張するにはどうすればよいですか?

4

3 に答える 3

10

個々のコンポーネントごとにカスタム レンダラーを自作する代わりに、メソッドをオーバーライドして要素名やコンポーネント インスタンスをチェックし、それに応じて追加の属性を書き込むRenderKitカスタムを提供する単一のレンダラーを作成することもできます。ResponseWriterstartElement()

HTML5 レンダー キットのキックオフ例を次に示します。

public class Html5RenderKit extends RenderKitWrapper {

    private RenderKit wrapped;

    public Html5RenderKit(RenderKit wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding) {
        return new Html5ResponseWriter(super.createResponseWriter(writer, contentTypeList, characterEncoding));
    }

    @Override
    public RenderKit getWrapped() {
        return wrapped;
    }

}

HTML5 応答ライター:

public class Html5ResponseWriter extends ResponseWriterWrapper {

    private static final String[] HTML5_INPUT_ATTRIBUTES = { "autofocus" };

    private ResponseWriter wrapped;

    public Html5ResponseWriter(ResponseWriter wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ResponseWriter cloneWithWriter(Writer writer) {
        return new Html5ResponseWriter(super.cloneWithWriter(writer));
    }

    @Override
    public void startElement(String name, UIComponent component) throws IOException {
        super.startElement(name, component);

        if ("input".equals(name)) {
            for (String attributeName : HTML5_INPUT_ATTRIBUTES) {
                String attributeValue = component.getAttributes().get(attributeName);

                if (attributeValue != null) {
                    super.writeAttribute(attributeName, attributeValue, null);
                }
            }
        }
    }

    @Override
    public ResponseWriter getWrapped() {
        return wrapped;
    }

}

実行するには、次の HTML5 レンダー キット ファクトリを作成します。

public class Html5RenderKitFactory extends RenderKitFactory {

    private RenderKitFactory wrapped;

    public Html5RenderKitFactory(RenderKitFactory wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public void addRenderKit(String renderKitId, RenderKit renderKit) {
        wrapped.addRenderKit(renderKitId, renderKit);
    }

    @Override
    public RenderKit getRenderKit(FacesContext context, String renderKitId) {
        RenderKit renderKit = wrapped.getRenderKit(context, renderKitId);
        return (HTML_BASIC_RENDER_KIT.equals(renderKitId)) ? new Html5RenderKit(renderKit) : renderKit;
    }

    @Override
    public Iterator<String> getRenderKitIds() {
        return wrapped.getRenderKitIds();
    }

}

に次のように登録しますfaces-config.xml

<factory>
    <render-kit-factory>com.example.Html5RenderKitFactory</render-kit-factory>
</factory>

JSF ユーティリティ ライブラリOmniFacesには、このようなレンダー キット、Html5RenderKit (ソース コードはこちら) もあり、理論的には PrimeFaces コンポーネントでも正常に動作するはずです。しかし、この質問により、私はもう一度見直さざるを得なくなり、component引数 inResponseWriter#startElement()nullinであることに気がつきませんでした<p:inputText>(の 74 行InputTextRendererを参照してください。代わりにあるべきでしwriter.startElement("input", inputText)た)。これが意図的なものなのか、PrimeFaces レンダラーの設計上の見落としなのかはわかりませんが、UIComponent#getCurrentComponent()代わりに使用して取得できます。


更新: これは OmniFaces 1.5 で修正されています。


今後の JSF 2.2 では、新しいpassthrough名前空間または<f:passThroughAttribute>タグを介してビューでカスタム属性を定義することがサポートされることに注意してください。JSF 2.2 の新機能も参照してください。- HTML5 パススルー属性

したがって、そう:

<html ... xmlns:p="http://java.sun.com/jsf/passthrough">
...
<h:inputText ... p:autofocus="true" />

( PrimeFaces のデフォルトの名前空間との衝突を避けるために、名前空間プレフィックスとしてaの代わりに使用することができます)p

または:

<h:inputText ...>
    <f:passThroughAttribute name="autofocus" value="true" />
</h:inputText>
于 2013-03-22T18:15:32.957 に答える
3

私が見つけた解決策は、入力レンダラーの encodeMarkup メソッドを拡張して再実装することでした。もっと一般的な解決策が欲しかったのですが、Primefaces のソース コードを調べたところ、コンポーネント レンダラーがカスタム属性を追加するための一般的なフックが見つかりませんでした。encodeMarkup(FacesContext context, InputText inputText)マークアップは、レンダラーのメソッドで書き出されます。へのクラス階層を呼び出しますがrenderPassThruAttributes(FacesContext context, UIComponent component, String[] attributes)、からの static final String[] 配列のみをフィードしますorg.primefaces.util.HTML

私の場合、InputMask、InputText、InputTextarea、および Password コンポーネントの「autofocus」属性のサポートが必要でした。さらに、各コンポーネントの実装は同じであるため、InputText コンポーネントに「オートフォーカス」を実装する方法について説明しますが、より多くの属性とコンポーネントをサポートするために拡張する方法は明らかです。

レンダラーを拡張/オーバーライドするには、Primefaces ソースを利用可能にし、encodeMarkup メソッドを見つけてその内容をコピーする必要があります。以下は、InputTextRenderer の例です。

protected void encodeMarkup(FacesContext context, InputText inputText) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = inputText.getClientId(context);

    writer.startElement("input", null);
    writer.writeAttribute("id", clientId, null);
    writer.writeAttribute("name", clientId, null);
    writer.writeAttribute("type", inputText.getType(), null);

    String valueToRender = ComponentUtils.getValueToRender(context, inputText);
    if(valueToRender != null) {
        writer.writeAttribute("value", valueToRender , null);
    }

    renderPassThruAttributes(context, inputText, HTML.INPUT_TEXT_ATTRS);

    if(inputText.isDisabled()) writer.writeAttribute("disabled", "disabled", null);
    if(inputText.isReadonly()) writer.writeAttribute("readonly", "readonly", null);
    if(inputText.getStyle() != null) writer.writeAttribute("style", inputText.getStyle(), null);

    writer.writeAttribute("class", createStyleClass(inputText), "styleClass");

    writer.endElement("input");
}

独自のレンダラーを拡張/オーバーライドする (重要なコードについてはコメントを参照してください):

public class HTML5InputTextRenderer extends InputTextRenderer {

    Logger log = Logger.getLogger(HTML5InputTextRenderer.class);

    //Define your attributes to support here
    private static final String[] html5_attributes = { "autofocus" };

    protected void encodeMarkup(FacesContext context, InputText inputText) throws IOException {
    ResponseWriter writer = context.getResponseWriter();
    String clientId = inputText.getClientId(context);

    writer.startElement("input", null);
    writer.writeAttribute("id", clientId, null);
    writer.writeAttribute("name", clientId, null);
    writer.writeAttribute("type", inputText.getType(), null);

    String valueToRender = ComponentUtils.getValueToRender(context, inputText);
    if (valueToRender != null) {
        writer.writeAttribute("value", valueToRender, null);
    }

    renderPassThruAttributes(context, inputText, HTML.INPUT_TEXT_ATTRS);

    //Make an extra call to renderPassThruAttributes with your own attributes array
    renderPassThruAttributes(context, inputText, html5_attributes);

    if (inputText.isDisabled())
        writer.writeAttribute("disabled", "disabled", null);
    if (inputText.isReadonly())
        writer.writeAttribute("readonly", "readonly", null);
    if (inputText.getStyle() != null)
        writer.writeAttribute("style", inputText.getStyle(), null);

    writer.writeAttribute("class", createStyleClass(inputText), "styleClass");

    writer.endElement("input");
    }
}

faces-config.xml でのレンダリング オーバーライドの構成

<?xml version='1.0' encoding='UTF-8'?>
<faces-config 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-facesconfig_2_0.xsd"
        version="2.0">

    <!-- snip... -->

    <render-kit>
        <renderer>
            <component-family>org.primefaces.component</component-family>
            <renderer-type>org.primefaces.component.InputTextRenderer</renderer-type>
            <renderer-class>com.mycompany.HTML5InputTextRenderer</renderer-class>
        </renderer>
    </render-kit>

    <!-- snip... -->
</faces-config>

念のため、web.xml で faces-config を構成していない場合は、次のように追加します。

<context-param>
        <param-name>javax.faces.CONFIG_FILES</param-name>
        <param-value>
            /WEB-INF/faces-config.xml, /faces-config.xml
        </param-value>
    </context-param>

次に、これをマークアップで使用するには:

<p:inputText id="activateUserName" value="${someBean.userName}" 
  autofocus="on">
</p:inputText> 

注: JSF は、値を持たない属性に満足していません。HTML5 の autofocus は値を使用しませんが、JSF は値が指定されていないとエラーをスローするため、そのような属性を追加するときは必ず破棄値を定義してください。

于 2013-03-22T16:21:13.937 に答える
2

JSF 2.2は、HTML5以降用に設計されたパススルー属性機能も提供するため、PrimeFacesがJSF 2.2を公式にサポートしている場合は、コンポーネントからhtml要素に任意の属性を渡すことができます。

于 2013-03-23T20:34:09.067 に答える