为Primefaces添加自定义属性(HTML5)支持(3.4)

Ric*_*ich 6 jsf html5 primefaces

在尝试在我的JSF/Primefaces Web应用程序中实现简单的html5属性"autofocus"时,我被提醒的事实是组件没有将所有未知属性传递给最终标记.我可以理解这个的原因,因为组件可能是html标记的复杂组合,如果属性尚未由组件定义,则不清楚放置属性的位置.

但对我来说最好的解决方案是支持自动对焦(以及我可能希望在我的应用程序中支持的任何其他可能类型的属性,这些属性尚未定义).

我已经看到为JSF 2.0 UIInput组件添加自定义属性(HTML5)支持,但这似乎适用于基本的JSF组件,并不适用于PrimeFaces组件.

如何扩展Primefaces的组件/渲染以支持此功能?

Bal*_*usC 10

您可以创建一个单独的方法,而不是为每个单独的组件自定义渲染器,您可以RenderKit提供一个自定义方法,ResponseWriter其中startElement()方法被覆盖以检查元素名称和/或组件实例,然后相应地编写其他属性.

这是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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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;
    }

}
Run Code Online (Sandbox Code Playgroud)

要使其运行,请创建此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();
    }

}
Run Code Online (Sandbox Code Playgroud)

并将其注册如下faces-config.xml:

<factory>
    <render-kit-factory>com.example.Html5RenderKitFactory</render-kit-factory>
</factory>
Run Code Online (Sandbox Code Playgroud)

JSF实用程序库OmniFaces也有这样的渲染工具包,Html5RenderKit(这里是源代码),理论上它也可以在PrimeFaces组件上正常工作.然而,这个问题迫使我再仔细看一下,我很不好意思地看到,component在争论ResponseWriter#startElement()是null在<p:inputText>(见行74InputTextRenderer,它应该是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" />
Run Code Online (Sandbox Code Playgroud)

(您可能希望使用a而不是p作为命名空间前缀来避免与PrimeFaces的默认命名空间冲突)

要么:

<h:inputText ...>
    <f:passThroughAttribute name="autofocus" value="true" />
</h:inputText>
Run Code Online (Sandbox Code Playgroud)