我正在用Mojarra JSF编写自定义表复合组件。我也试图将该组合绑定到支持组件。目的是能够指定表在复合属性中具有的元素数量,稍后绑定的后备组件将在呈现视图之前自动生成元素本身。我有这个示例代码:
主页:
<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>
my table.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
方法。
如果我按照wiki page进行复合组件,我可以用这种方式工作,但每次调用List
时都必须初始化getValues()
,将逻辑引入getter方法:-(。
有什么想法吗?这似乎是一个与重写encodeBegin
方法有关的麻烦。我也尝试在markInitialState
上初始化它,但那里的属性还没有...
测试了Mojarra 2.1.27 + Tomcat 6-7和Mojarra 2.2.5 + Tomcat 7
至于原因,UIComponent
实例本身就是请求作用域。回发有效地创建了一个全新的实例,其中包含values
重新初始化为default的属性。在您的实现中,它仅在encodeXxx()
期间填充,decode()
在@PostConstruct
之后很久就被调用,其中动作事件需要排队并因此太晚。
您最好在组件初始化期间填充它。如果你想为UIComponent
实例提供类似postAddToView
的钩子,那么<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()
方法,如果它没有做任何有用的东西)
另一种方法是在values
方法中进行延迟初始化。
一个更简单的解决方案是存储和检索encodeBegin
作为组件状态的一部分。存储可以在@FacesComponent("components.myTable")
public class TestTable extends UINamingContainer {
public void action() {
System.out.println("Called");
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
// Initialize the list according to the element number
List<String> values = new ArrayList<>();
Integer num = (Integer) getAttributes().get("itemNumber");
for (int i = 0; i < num; i++) {
values.add("item" + i);
}
getStateHelper().put("values",values);
super.encodeBegin(context);
}
public List<String> getValues() {
return (List<String>)getStateHelper().get("values");
}
}
期间发生,并且检索可以直接在getter中发生:
getValues()
为了避免重复qazxswpoi中的逻辑,在更复杂的情况下可能需要额外的解析,应该有一种方法在属性可用后立即处理和缓存,尽管我不知道何时以及如何在这一点上。
无论哪种方式 - 这似乎是解决这个问题的最简单方法。