在JSF中为Primefaces自动完成组件添加自定义属性

7
我曾在另一个问题中询问过有关通过属性的问题,并发现我可以为<p:autocomplete>组件创建自定义渲染器,但问题是我的自定义渲染器将用于我的项目中的每个p:autocomplete(整个网站)。因此,我决定创建一个扩展了org.primefaces.component.autocomplete.AutoComplete的自定义组件,并向文本框添加必要的属性。

我最初的想法是添加一个构造函数,但似乎不起作用,因为此时属性映射为空:

@FacesComponent("com.mycomponents.SiteSearch")
public class SiteSearch extends AutoComplete {

    public SiteSearch() {
        Map<String,Object> attrs = getAttributes();
        attrs.put("x-webkit-speech", null); 
        attrs.put("x-webkit-grammer", "builtin:search");
        attrs.put("onwebkitspeechchange", "this.form.submit();");
        attrs.put("placeholder", "Enter a Search Term");
    }   
}

我的另一个想法是让这个自定义组件保持空置(空类),然后指定一个自定义渲染器,它继承 org.primefaces.component.autocomplete.AutoCompleteRenderer 并在那里修改属性。

说了那么多,我只需要一种方法来将这些属性与这个文本框分开,所以仅仅在 p:autoComplete 上放置一个自定义渲染器是不可行的(除非也许我可以为这个 p:autoComplete 使用 renderType 属性?)。


正在看电视剧(现在是《豪斯医生》)。请稍等片刻 :) - BalusC
哈哈,没问题。我觉得每当我看到一个JSF的问题时,我就会自动地认为你的答案会是正确的,甚至不用看一眼。有人能说JSF忍者吗? - Adam
1个回答

18
如果您需要使用不同于<p:autoComplete>的渲染器实现特定组件,那么您真的无法创建具有自己的family和component type的自定义组件。但是,您仍然可以扩展PrimeFaces AutoComplete(以及其渲染器)以节省一些样板代码。
在自定义组件中,您需要为这些属性提供getter方法。您也可以指定setter方法,这样您就可以始终从视图端重写默认值。这些getter/setter方法应该将操作委托给StateHelper
只有x-webkit-*属性存在一些问题。在Java标识符中,-是一个非法字符。因此,您必须重新命名getter/setter方法,并在某种程度上更改渲染器,因为标准渲染器依赖于组件属性名称与标签属性名称完全相同。更新:我了解到x-webkit-speech应该按原样呈现(因此,不需要getter/setter方法),而x-webkit-grammer实际上是一个拼写错误,应该是x-webkit-grammar
以下是SiteSearch组件的示例。
@FacesComponent(SiteSearch.COMPONENT_TYPE)
public class SiteSearch extends AutoComplete {

    public static final String COMPONENT_FAMILY = "com.example";
    public static final String COMPONENT_TYPE = "com.example.SiteSearch";

    private enum PropertyKeys {
        grammar, onspeechchange, placeholder
    }

    @Override
    public String getFamily() {
        return COMPONENT_FAMILY;
    }

    @Override
    public String getRendererType() {
        return SiteSearchRenderer.RENDERER_TYPE;
    }

    public String getGrammar() {
        return (String) getStateHelper().eval(PropertyKeys.grammar, "builtin:search");
    }

    public void setGrammar(String grammar) {
        getStateHelper().put(PropertyKeys.grammar, grammar);
    }

    public String getOnspeechchange() {
        return (String) getStateHelper().eval(PropertyKeys.onspeechchange, "submit()");
    }

    public void setOnspeechchange(String onspeechchange) {
        getStateHelper().put(PropertyKeys.onspeechchange, onspeechchange);
    }

    public String getPlaceholder() {
        return (String) getStateHelper().eval(PropertyKeys.placeholder, "Enter a Search Term");
    }

    public void setPlaceholder(String placeholder) {
        getStateHelper().put(PropertyKeys.placeholder, placeholder);
    }

}
请注意,所有getter都指定了默认值。如果eval()返回null,则将返回默认值。我还使属性名称有些中性化,这样只需相应地修改渲染器即可在未来的非webkit浏览器中重用它。
以下是上述组件所需的SiteSearchRenderer渲染器的外观:
@FacesRenderer(
    componentFamily=SiteSearch.COMPONENT_FAMILY,
    rendererType=SiteSearchRenderer.RENDERER_TYPE
)
public class SiteSearchRenderer extends AutoCompleteRenderer {

    public static final String RENDERER_TYPE = "com.example.SiteSearchRenderer";

    @Override
    protected void renderPassThruAttributes(FacesContext facesContext, UIComponent component, String[] attrs) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.writeAttribute("x-webkit-speech", "x-webkit-speech", null);
        writer.writeAttribute("x-webkit-grammar", component.getAttributes().get("grammar"), "grammar");
        writer.writeAttribute("onwebkitspeechchange", component.getAttributes().get("onspeechchange"), "onspeechchange");
        writer.writeAttribute("placeholder", component.getAttributes().get("placeholder"), "placeholder");
        super.renderPassThruAttributes(facesContext, component, attrs);
    }

}

为在视图中使用它,我们当然需要将它作为标签进行注册。创建一个/WEB-INF/my.taglib.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib
    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"
    version="2.0"
>
    <namespace>http://example.com/ui</namespace>

    <tag>
        <tag-name>siteSearch</tag-name>
        <component>
            <component-type>com.example.SiteSearch</component-type>
            <renderer-type>com.example.SiteSearchRenderer</renderer-type>
        </component>
    </tag>
</facelet-taglib>
请注意,您不再需要在faces-config.xml中使用<renderer>了。现在@FacesRenderer注释可以在真正的自定义组件上发挥作用。因此,请从faces-config.xml中删除您根据先前问题创建的<renderer>条目。
现在,请通过以下方式在web.xml中设置上下文参数,以告知JSF您拥有一个自定义标签库:
<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/my.taglib.xml</param-value>
</context-param>

最后,您可以按如下方式使用它:

<html ... xmlns:my="http://example.com/ui">
...
<my:siteSearch />

你甚至可以指定其他属性,这些属性将覆盖组件中设置的默认值:

<my:siteSearch grammar="builtin:language" onspeechchange="alert('peek-a-boo')" placeholder="Search" />

为了在IDE中自动补全属性,您需要将每个属性单独指定为<tag>声明中的<attribute>。在my.taglib.xml中。


@BalusC,如何为我使用类似于 https://stackoverflow.com/questions/44454804/composite-attributes-returns-null-in-jsf-custom-components 给出的组合创建的自定义组件实现相同的功能? - Sasi Dhivya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接