Java 在请求范围内动态呈现JSF 2内容-在动态内容中未调用侦听器

Java 在请求范围内动态呈现JSF 2内容-在动态内容中未调用侦听器,java,jsf,jsf-2,java-ee-6,myfaces,Java,Jsf,Jsf 2,Java Ee 6,Myfaces,我把我的问题案例放在下面,这简化了我遇到的一个现实世界的问题。通过将Bean更改为@SessionScoped,我可以解决这个问题,但这确实是不可取的。正如@ViewScoped。我认为它应该在@RequestScoped中工作。我的问题是: 为什么JSF需要访问{simpleBean.outerStrings}来调用{simpleBean.processInnerClick()}侦听器(outerStrings将在上一个请求中失效) 解决这个问题的最佳模式是什么 很高兴编辑,以使更清楚-只

我把我的问题案例放在下面,这简化了我遇到的一个现实世界的问题。通过将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;
    }

}
import javax.enterprise.context.requestscope;
导入javax.enterprise.context.SessionScoped;
导入javax.inject.Named;
@命名
@请求范围
公共类SimpleBean实现了可序列化{
私有列表TopLevelString=
数组.asList(“TopLevel1”、“TopLevel2”);
私有列表外串;
私有列表字符串;
public void processOuterClick(){
System.err.println(“processOuterClick()”;
outerString=Arrays.asList(“Outer1”、“Outer2”、“Outer3”);
}
public void processInnerClick(){
System.err.println(“processInnerClick()”;
innerStrings=Arrays.asList(“InnerA”、“InnerB”、“InnerC”);
}
公共列表GetOuterString(){
返回外串;
}
公共列表getInnerStrings(){
返回内部字符串;
}
公共列表GetToLevel字符串(){
返回TopLevelString;
}
}
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>

#{toplevel}
#{outerLevel}
#{innerLevel}
基本上:

  • #{simpleBean.processOuterClick()}
    侦听器触发ok,并呈现#{outerLevel}命令链接
  • 但是,当我单击
    {outerLevel}
    命令链接时,
    {simpleBean.processInnerClick()}
    侦听器从未被触发

对于此特定需求,请求范围是错误的。一个请求范围的bean在响应结束时被丢弃,在随后的请求中将创建一个新的bean,其所有属性都设置为默认值。在JSF2术语中,您确实需要视图范围。会话范围确实太宽,对于这种需求来说比请求范围要糟糕得多(当最终用户在多个窗口/选项卡中打开同一页时,他们将在物理上共享同一个bean,当最终用户在交互后在视图之间切换时,导致每个视图的行为不直观)

要解决特定问题,您只需使用:

由于某种原因,您似乎更喜欢CDI管理而不是JSF管理,因此可以选择CDI替代
@ViewScoped

你只需要管理对话的开始和结束

另见:

感谢您的回复-但是为什么Jsf需要访问外部字符串?内部单击的侦听器没有引用它,我也没有要求Ajax调用评估使用外部字符串进行渲染的组件(至少是直接评估),它需要它来找到与操作关联的组件,以便可以调用它。另请参见第一个“另请参见”链接。另一种方法是使用
来传递请求参数,并在(post)构造函数中准确地准备所需的模型。也看到了哦,好的,我看到了。谢谢我认为jsf不太适合这样的问题——它有组件树,我只想渲染一个新组件,而不是检查渲染原始视图时使用的模型数据是否仍然存在。最终的结果是,我只想在DOM中保存的数据必须比我想在服务器上保存的时间长。在基于动作的框架中,我将使用Ajax调用服务器,评估JSP并将输出发送回。然后,我会将内容附加到dom元素中。对于我的需求,这正是我想要jsf做的——我不希望Ajax调用需要之前使用的服务器上的整个数据集
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class SimpleBean implements Serializable {
    // ...
}
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";
    }

    // ...
}