我已将我的问题案例放在下面,这简化了我遇到的现实问题。通过将 Bean 更改为 @SessionScoped 我可以解决这个问题,但这确实是不可取的。 @ViewScoped 也是如此。我认为它应该在@RequestScoped 中工作。我的问题是:
- 为什么 JSF 需要访问
#{simpleBean.outerStrings}
来调用#{simpleBean.processInnerClick()}
监听器(outerStrings 将在上一个请求中死亡) ) - 解决这个问题的最佳模式是什么?
很高兴进行编辑以使其更加清晰 - 请告诉我。
Bean类
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named
@RequestScoped
public class SimpleBean implements Serializable {
private List<String> topLevelStrings =
Arrays.asList("TopLevel1", "TopLevel2");
private List<String> outerStrings;
private List<String> innerStrings;
public void processOuterClick() {
System.err.println("processOuterClick()");
outerStrings = Arrays.asList("Outer1", "Outer2", "Outer3");
}
public void processInnerClick() {
System.err.println("processInnerClick()");
innerStrings = Arrays.asList("InnerA", "InnerB", "InnerC");
}
public List<String> getOuterStrings() {
return outerStrings;
}
public List<String> getInnerStrings() {
return innerStrings;
}
public List<String> getTopLevelStrings() {
return topLevelStrings;
}
}
XHTML
<h:form>
<ui:repeat var="toplevel" value="#{simpleBean.topLevelStrings}"
id="toplevel_id">
<h:commandLink id="toplevel_command">
#{toplevel} <br/>
<f:ajax render="outerlevel_panel"
listener="#{simpleBean.processOuterClick()}"/>
</h:commandLink>
<h:panelGroup id="outerlevel_panel">
<ui:repeat var="outerLevel" value="#{simpleBean.outerStrings}"
id="outerlevel_id">
<h:commandLink id="outerlevel_command">
#{outerLevel} <br/>
<f:ajax listener="#{simpleBean.processInnerClick()}" render="innerlevel_panel"/>
</h:commandLink>
<h:panelGroup id="innerlevel_panel">
<ui:repeat var="innerLevel" value="#{simpleBean.innerStrings}"
id="innerlevel_id">
<h:commandLink id="innerlevel_command">
#{innerLevel} <br/>
</h:commandLink>
</ui:repeat>
</h:panelGroup>
</ui:repeat>
</h:panelGroup>
</ui:repeat>
</h:form>
基本上:
#{simpleBean.processOuterClick()}
监听器正常触发,并且 #{outerLevel} 命令链接呈现- 但是当我单击
#{outerLevel}
命令链接时,#{simpleBean.processInnerClick()}
监听器永远不会触发
最佳答案
对于此特定要求,请求范围是错误的范围。请求范围的 bean 在响应结束时将被垃圾化,并且将在后续请求中创建一个新的 bean,并将其所有属性设置为默认值。用 JSF2 术语来说,您确实需要 View 范围。 session 范围确实太宽泛了,并且对于这一要求来说比请求范围更糟糕(当最终用户在多个窗口/选项卡中打开同一页面时,他们将在物理上共享同一个 bean,从而导致每次 View 行为不直观)最终用户在交互后在 View 之间切换)。
要解决您的特定问题,您所需要做的就是使用 @ManagedBean
@ViewScoped
:
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
@ManagedBean
@ViewScoped
public class SimpleBean implements Serializable {
// ...
}
由于某种原因,您似乎更喜欢 CDI 管理而不是 JSF 管理,因此 @ViewScoped
的 CDI 替代方案是 @ConversationScoped
.
import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;
@Named
@ConversationScoped
public class SimpleBean implements Serializable {
@Inject
private Conversation conversation;
@PostConstruct
public void init() {
conversation.begin();
}
public String navigateToOtherPage() {
conversation.end();
return "otherPage?faces-redirect=true";
}
// ...
}
您只需管理对话的开始和结束 yourself .
另请参阅:
关于java - 在请求范围内动态呈现 JSF 2 内容 - 在动态内容中时不会调用监听器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8883551/