我可以告诉JSF f:属性是否适用于我的复合组件的某些部分?

时间:2012-03-02 14:06:09

标签: java jsf-2 attributes composite-component

在下面的示例中,我是否能以某种方式告诉JSF <f:attribute>适用于某个特定组件,就像我可以在for="..."<f:convertNumber for="...">中使用<f:validator for="...">一样? / p>

<mytags:myCcInputWithValueHolder id="myparent" item="#{myBean.myDouble}" >
    <f:convertNumber minFractionDigits="2" for="myinput"/>
    <f:validator validatorId="bindableDoubleRangeValidator" for="myinput"/>
    <f:attribute name="minimum" value="#{30.00}"/>
    <f:attribute name="maximum" value="#{39.99}"/>
</mytags:myCcInputWithValueHolder>


背景:

关注BalusC's solution to a JSF issue,我使用自定义验证程序。要使用某些参数为该验证程序提供信息,请使用<f:attribute>

接下来,当使用带有EditableValueHolder的复合组件时,我可以(实际上:必须)将验证器分配给实际的h:inputText。但是我没有为f:attribute做同样的事情,所以这些被添加到调用父组件中。例如:

<composite:attribute name="item" .../>
<composite:editableValueHolder name="myinput" targets="myinputtext"/>        
:
<h:inputText id="myinputtext" value="#{cc.attrs.item}">
    <!-- <composite:insertChildren /> doesn't change anything -->
</h:inputText>

...与此帖子顶部显示的<f:validator for="myinput" ...>一起使用,将验证程序绑定到myparent:myinputtext,但属性绑定到myparent


解决方法:

documentation for <f:attribute> indeed states

  

将属性添加到与最近的父UIComponent自定义操作关联的UIComponent。

鉴于此,以下复合组件也按预期工作:

<composite:attribute name="item" .../>
<composite:attribute name="min" .../>
<composite:attribute name="max" .../>
:
<h:inputText id="myinput" value="#{cc.attrs.item}">
    <f:convertNumber minFractionDigits="2"/>
    <f:validator validatorId="bindableDoubleRangeValidator"/>
    <f:attribute name="minimum" value="#{cc.attrs.min}"/>
    <f:attribute name="maximum" value="#{cc.attrs.max}"/>
</h:inputText>

...与:

<mytags:myCcInputWithValidator 
   item="#{myBean.myDouble}" min="#{30.00}" max="#{39.99}"/>

此外,我可以轻松扩展BalusC's BindableDoubleRangeValidator以递归到父组件中以获取值:

Object getAttribute(FacesContext c, UIComponent component, String name) {
    Object result = component.getAttributes().get(name);
    if (result == null && component.getParent() != null) {
        result = getAttribute(c, component.getParent(), name);
    }
    return result;
}

仍然:任何更好的解决方案?

1 个答案:

答案 0 :(得分:3)

这确实令人讨厌。 <f:attribute>确实特定于最近的父UIComponent。如果将它嵌套在复合中,则它将特定于复合组件本身。一种方法是将属性从复合复制到相关的输入组件:

<h:inputText id="myinputtext" value="#{cc.attrs.item}">
    <f:attribute name="minimum" value="#{cc.attrs.minimum}"/>
    <f:attribute name="maximum" value="#{cc.attrs.maximum}"/>
</h:inputText>

这样您问题中的第一个代码段就能正常工作。

关于您的上一个代码段,更简单的方法是通过UIComponent#getCompositeComponentParent()获取复合组件父级。

Object getCompositeParentAttribute(UIComponent component, String name) {
    UIComponent composite = UIComponent.getCompositeComponentParent(component);
    return composite.getAttributes().get(name);
}

毕竟,我只是选择了一个新的复合材料,如果需要,可以在<ui:decorate>的帮助下使用与原始复合材料相同的模板。复合材料旨在最大限度地减少重复的样板。

<my:inputDouble value="..." min="..." max="..." />

<cc:implementation>
    <ui:decorate template="/WEB-INF/templates/someCommonTemplate.xhtml">
        <ui:define name="input">
            <h:inputText id="myinput" value="#{cc.attrs.value}">
                <f:convertNumber minFractionDigits="2" />
                <f:validator validatorId="bindableDoubleRangeValidator"/>
                <f:attribute name="minimum" value="#{cc.attrs.min}"/>
                <f:attribute name="maximum" value="#{cc.attrs.max}"/>
            </h:inputText>
        </ui:define>
    </ui:decorate>
</cc:implementation>