在下面的示例中,我是否能以某种方式告诉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;
}
仍然:任何更好的解决方案?
答案 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>