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

发布于 2024-12-27 12:58:03 字数 2710 浏览 4 评论 0原文

我将我的问题案例放在下面,这简化了我遇到的现实问题。通过将 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()} 监听器永远不会被触发

I have put my problem case below, which simplifies a real world problem I am having. By changing the Bean to @SessionScoped I can solve this, but that is really undesirable. As is @ViewScoped. I think it should work in @RequestScoped. My questions are:

  • Why does JSF need access to #{simpleBean.outerStrings} to invoke the #{simpleBean.processInnerClick()} listener (outerStrings will have died in the previous request)
  • What is the best pattern for solving this?

Happy to edit to make clearer - just let me know.

Bean class

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>

Basically:

  • The #{simpleBean.processOuterClick()} listener fires ok and the #{outerLevel} command links render
  • But when I click on the #{outerLevel} command Link the #{simpleBean.processInnerClick()} listener is never fired

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鸠书 2025-01-03 12:58:03

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

要解决您的特定问题,您所需要做的就是使用 @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";
    }

    // ...
}

您只需管理对话的开始和结束 你自己

另请参阅:

The request scope is the wrong scope for this particular requirement. A request scoped bean is garbaged by end of the response and a new one will be created in the subsequent request with all of its properties set to default. In JSF2 terms, you really need the view scope. The session scope is indeed too broad and much worse for this requirement than the request scope (when an enduser opens the same page in multiple windows/tabs, they will share physically the one and same bean, resulting in unintuitive per-view behaviour when the enduser switches between views after interactions).

All you need to do to fix your particular problem is to use @ManagedBean @ViewScoped:

import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;

@ManagedBean
@ViewScoped
public class SimpleBean implements Serializable {
    // ...
}

As you seem to prefer CDI management over JSF management for some reason, the CDI alternative to @ViewScoped is @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";
    }

    // ...
}

You only have to manage the begin and end of the conversation yourself.

See also:

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文