问题描述
我正在用Mojarra JSF编写我的自定义表复合组件.我还试图将复合材料绑定到支持组件.目的是能够指定表在复合属性中具有的元素数,稍后在绑定支持组件将在呈现视图之前自动生成元素本身.我有以下示例代码:
I'm writing my custom table composite component with Mojarra JSF. I'm also trying to bind that composite to a backing component. The aim is to be able to specify the number of elements the table has in a composite attribute, later on the bound backing component will autogenerate the elements itself before view gets rendered. I've this sample code:
主页:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:comp="http://java.sun.com/jsf/composite/comp">
<h:head />
<body>
<h:form>
<comp:myTable itemNumber="2" />
</h:form>
</body>
</html>
myTable.xhtml:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:composite="http://java.sun.com/jsf/composite"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<composite:interface componentType="components.myTable">
<composite:attribute name="itemNumber"
type="java.lang.Integer" required="true" />
</composite:interface>
<composite:implementation>
<h:dataTable value="#{cc.values}" var="value">
<h:column headerText="column">
#{value}
<h:commandButton value="Action" action="#{cc.action}" />
</h:column>
</h:dataTable>
</composite:implementation>
</h:body>
</html>
MyTable.java:
@FacesComponent("components.myTable")
public class MyTable extends UINamingContainer {
private List<String> values = new ArrayList<String>();
public void action() {
System.out.println("Called");
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
// Initialize the list according to the element number
Integer num = (Integer) getAttributes().get("itemNumber");
for (int i = 0; i < num; i++) {
values.add("item" + i);
}
super.encodeBegin(context);
}
public List<String> getValues() {
return values;
}
}
问题是表格可以正确渲染(在这种情况下有两个项目),但是当按行上的按钮时,action
方法不会被调用.
The issue is table gets rendered properly (in this case with two items), but action
method doesn't get called when pressing the button on the lines.
如果遵循复合组件的 wiki页面,我可以使它以这种方式工作,但是在每次调用getValues()
时初始化List
,将逻辑引入getter方法:-(.
If I follow the wiki page for composite components, I can get it work in that way, but having to initialize the List
each time getValues()
is called, introducing logic into the getter method :-(.
有什么想法吗?与覆盖 encodeBegin
方法.我还尝试在上对其进行初始化. markInitialState
,但该属性尚不可用...
Any idea about that? It seems to be a trouble related with overriding encodeBegin
method. I also tried initializing it on markInitialState
, but attributes are not yet available there...
使用Mojarra 2.1.27 + Tomcat 6-7& Mojarra 2.2.5 + Tomcat 7
推荐答案
对于原因,UIComponent
实例本质上是请求范围的.回发有效地创建了一个全新的实例,该实例具有重新设置为默认值的values
之类的属性.在您的实现中,它仅在encodeXxx()
期间填充,该调用在decode()
之后很长一段时间内调用,其中动作事件需要排队,因此为时已晚.
As to the cause, UIComponent
instances are inherently request scoped. The postback effectively creates a brand new instance with properties like values
reinitialized to default. In your implementation, it is only filled during encodeXxx()
, which is invoked long after decode()
wherein the action event needs to be queued and thus too late.
您最好在组件初始化期间填充它.如果要为UIComponent
实例使用类似@PostConstruct
的钩子,则postAddToView
事件是一个不错的选择.在将组件实例添加到组件树之后,将直接调用该函数.
You'd better fill it during the initialization of the component. If you want a @PostConstruct
-like hook for UIComponent
instances, then the postAddToView
event is a good candidate. This is invoked directly after the component instance is added to the component tree.
<cc:implementation>
<f:event type="postAddToView" listener="#{cc.init}" />
...
</cc:implementation>
使用
private List<String> values;
public void init() {
values = new ArrayList<String>();
Integer num = (Integer) getAttributes().get("value");
for (int i = 0; i < num; i++) {
values.add("item" + i);
}
}
(如果不再有用,请删除encodeBegin()
方法)
另一种选择是getValues()
方法中的延迟初始化.
An alternative would be lazy initialization in getValues()
method.
这篇关于根据提供的属性初始化复合组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!