Jsf 根据提供的属性初始化复合组件
我正在用MojarraJSF编写我的定制表复合组件。我还尝试将该组合绑定到支持组件。其目的是能够指定表在复合属性中的元素数量,稍后绑定的支持组件将在呈现视图之前自动生成元素本身。我有以下示例代码: 主页:Jsf 根据提供的属性初始化复合组件,jsf,jsf-2,composite-component,Jsf,Jsf 2,Composite Component,我正在用MojarraJSF编写我的定制表复合组件。我还尝试将该组合绑定到支持组件。其目的是能够指定表在复合属性中的元素数量,稍后绑定的支持组件将在呈现视图之前自动生成元素本身。我有以下示例代码: 主页: <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=
<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>
<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>
@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;
}
}
myTable.xhtml:
<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>
<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>
@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;
}
}
#{value}
MyTable.java:
<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>
<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>
@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;
}
}
@FacesComponent(“components.myTable”)
公共类MyTable扩展了UINamingContainer{
私有列表值=新的ArrayList();
公共无效行动(){
System.out.println(“被调用”);
}
@凌驾
public void encodeBegin(FacesContext上下文)引发IOException{
//根据元素编号初始化列表
整数num=(整数)getAttributes().get(“itemNumber”);
for(int i=0;i
问题是,表格正确呈现(在本例中有两项),但按下行上的按钮时,action
method不会被调用
如果我遵循for composite components,我可以让它以这种方式工作,但是每次调用getValues()
时都必须初始化列表,从而将逻辑引入getter方法:-(
知道吗?这似乎是一个与重写方法有关的问题。我也尝试过初始化它,但属性在那里还不可用
使用Mojarra 2.1.27+Tomcat 6-7和Mojarra 2.2.5+Tomcat 7进行测试,关于原因,UIComponent
实例固有的请求范围。回发有效地创建了一个全新的实例,其属性如值
重新初始化为默认值。在您的实现中,它仅在encodeXxx()期间填充
,在decode()
很久之后调用,其中操作事件需要排队,因此太晚了
您最好在组件初始化期间填充它。如果您想为UIComponent
实例创建一个类似@PostConstruct
的钩子,那么postAddToView
事件是一个很好的候选事件。这将在组件实例添加到组件树后直接调用
<cc:implementation>
<f:event type="postAddToView" listener="#{cc.init}" />
...
</cc:implementation>
...
与
私有列表值;
公共void init(){
值=新的ArrayList();
整数num=(整数)getAttributes().get(“值”);
for(int i=0;i
(如果encodeBegin()
方法不再有用,请将其删除)
另一种方法是在getValues()
方法中进行延迟初始化。更简单的解决方案是将值作为组件状态的一部分进行存储和检索。存储可以在encodeBegin
期间进行,检索可以直接在getter中进行:
@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");
}
}
@FacesComponent(“components.myTable”)
公共类TestTable扩展了UINamingContainer{
公共无效行动(){
System.out.println(“被调用”);
}
@凌驾
public void encodeBegin(FacesContext上下文)引发IOException{
//根据元素编号初始化列表
列表值=新的ArrayList();
整数num=(整数)getAttributes().get(“itemNumber”);
for(int i=0;i
为了避免重复getValues()
中的逻辑,在更复杂的情况下可能需要额外的解析,应该有一种在属性可用后立即处理和缓存属性的方法,尽管目前我不确定何时以及如何处理
无论哪种方式-这似乎是解决此问题的最简单方式。再次感谢@BalusC。我不知道UIComponent
是无状态的,但它确实有意义。作为保持状态的解决方案,我使用了一个列表
引用,它由@ViewScoped
托管bean和共享wi创建组件本身;-)这种解决方法似乎有缺点。我通常使用preRenderView
方法初始化托管bean。我不使用@PostConstruct
,因为我处理的是必须在确定加载内容之前设置的视图参数postAddToView
似乎是在这之前被调用的,所以我的托管bean还没有显示这些数据。有没有其他事件可以替代它?preRenderView
应该可以,只要在FacesContext\isPostback()
为true
时跳过它。bean是视图范围的,对吗?使用preRenderView
事件会导致调用组件备份的两个部分:初始化一个后,调用另一个的getter,始终返回null
。因此,我最终在getter方法中进行了延迟初始化。仍然需要对其进行更深入的测试,如果我在测试上下文中重播该问题,将尝试提供一个基本案例。@BalusC-使用getstateheloper()
将值作为组件状态的一部分似乎更简单。在我的回答中,在修改encodeBegin
和getValues
以从缓存中存储和检索后,组件似乎工作正常。