【发布时间】:2020-03-21 15:11:21
【问题描述】:
我有一个实现UIInput 的自定义组件,它需要保存一些状态信息以供以后在回发请求中重用。独立使用它工作正常,但在<ui:repeat> 内,回发会找到最新呈现的数据行的保存状态。动作调用的日志输出是
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
我期望的地方
INFORMATION: myData is "first foo"
INFORMATION: myData is "second foo"
INFORMATION: myData is "third foo"
INFORMATION: ok action
我知道myComponent 是ui:repeat 内部的一个实例。那么保存组件状态以便为数据集中的每一行正确恢复它的最佳方法是什么?
我的 XHTML 表单:
<h:form>
<ui:repeat var="s" value="#{myController.data}">
<my:myComponent data="#{s}"/>
</ui:repeat>
<h:commandButton action="#{myController.okAction}" value="ok">
<f:ajax execute="@form" render="@form"/>
</h:commandButton>
</h:form>
我的豆子:
@Named
@ViewScoped
public class MyController implements Serializable {
private static final long serialVersionUID = -2916212210553809L;
private static final Logger LOG = Logger.getLogger(MyController.class.getName());
public List<String> getData() {
return Arrays.asList("first","second","third");
}
public void okAction() {
LOG.info("ok action");
}
}
复合组件 XHTML 代码:
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:cc="http://xmlns.jcp.org/jsf/composite">
<cc:interface componentType="myComponent">
<cc:attribute name="data"/>
</cc:interface>
<cc:implementation>
<h:panelGrid columns="2">
<h:outputLabel value="cc.attrs.data"/>
<h:outputText value="#{cc.attrs.data}"/>
<h:outputLabel value="cc.myData"/>
<h:outputText value="#{cc.myData}"/>
</h:panelGrid>
</cc:implementation>
</ui:component>
复合组件支持类:
@FacesComponent
public class MyComponent extends UIInput implements NamingContainer {
private static final Logger LOG=Logger.getLogger(MyComponent.class.getName());
public String calculateData() {
return String.format("%s foo", this.getAttributes().get("data") );
}
public String getMyData() {
return (String)getStateHelper().get("MYDATA");
}
public void setMyData( String data ) {
getStateHelper().put("MYDATA", data);
}
@Override
public String getFamily() {
return UINamingContainer.COMPONENT_FAMILY;
}
@Override
public void encodeBegin(FacesContext context) throws IOException {
this.setMyData( calculateData() );
super.encodeBegin(context);
}
@Override
public void processDecodes(FacesContext context) {
super.processDecodes(context);
LOG.log(Level.INFO, "myData {0}", getMyData() );
}
}
【问题讨论】:
-
我不确定“正确的方法”是什么,我可以想到一些解决方法:1)使用
"MYDATA" + getClientId()作为状态键或 2)使用@987654332 @ 而不是getStateHelper()或 3) 使用c:forEach而不是ui:repeat(这会导致完全不同的组件树,正如 Kukeltje 会在此处指出的那样;p)但是(3)在使用时没有帮助您在 dataTable 中的组件。 -
感谢@Selaron,但在这种特殊情况下,我真的在寻找“正确的方法”。
c:forEach和getAttributes在我们的例子中不是解决方法,使用getClientId()是一个很好的解决方法。但是,真的,这有点难看,不是吗? -
是的,它很丑。我很高兴看到这个问题得到解答,因为到目前为止我必须解决这个问题。
-
这种方法既奇怪又令人困惑。我的第一次尝试是使用
getStateHelper()而不是this.getAttributes(),因为getStateHelper()会知道迭代上下文。 -
@BalusC 好吧,
getStateHelper()的行为是我的问题。这里我们有两个请求。在第一个中,组件被编码,我们使用encodeBegin将myData放入stateHelper。在第二个请求(回发)中,组件被解码。在processDecodes中,stateHelper向我们展示了意想不到的数据。这就是我的问题:如何以正确的方式使用stateHelper,以便即使在回发请求中它也能知道迭代器?根据我的预期,它应该开箱即用 - 但事实并非如此。
标签: jsf