【问题标题】:Backing bean in composite component is recreated on every request在每个请求上重新创建复合组件中的支持 bean
【发布时间】:2023-03-11 14:57:01
【问题描述】:

我有两个变量“userId”和“name”。例如,当我单击“SHOW USERID”按钮时,它可以正常工作并设置“renderUserId=true”,并使用“render”显示它,但是如果我单击另一个“SHOW”按钮,Bean 会重建并且我会松动“renderUserId=true”,它变成“false”和“renderName=true”,所以它显示正常,但 USERID 被隐藏了。

我的问题是,如何避免在渲染 xhtml 时丢失 bean 值?

这是我的代码的简单模拟。

注意:如果我在“h:commandButton”中使用“actionListener”而不是“f:setPropertyActionListener”,我会得到相同的结果,bean 是重构的

example.xhtml

<ui:composition xmlns="http://www.w3.org/1999/xhtml"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:cc="http://java.sun.com/jsf/composite">

    <cc:interface componentType="com.bean.ExampleBean">
        <cc:attribute name="userId" type="java.lang.Integer"/>
        <cc:attribute name="name" type="java.lang.String"/>
    </cc:interface>

    <cc:implementation>

        <h:panelGrid id="example_panel" columns="1" width="100%">

            <h:outputText value="USERID: #{cc.attrs.userId}" rendered="#{!cc.attrs.renderUserId}"/>

            <a4j:commandButton value="SHOW USERID" render="example_panel"
                rendered="#{!cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{true}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE USERID" render="example_panel"
                rendered="#{cc.attrs.renderUserId}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderUserId}"/>
            </a4j:commandButton>                


            <h:outputText value="NAME: #{cc.attrs.name}" rendered="#{!cc.attrs.renderName}"/>

            <a4j:commandButton value="SHOW NAME" render="example_panel"
                rendered="#{!cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                
            <a4j:commandButton value="HIDE NAME" render="example_panel"
                rendered="#{cc.attrs.renderName}">
                <f:setPropertyActionListener value="#{false}"
                    target="#{cc.attrs.renderName}"/>
            </a4j:commandButton>                


        </h:panelGrid>

    </cc:implementation>

</ui:composition>

ExampleBean.java

import javax.faces.component.FacesComponent;
import javax.faces.component.UINamingContainer;


@FacesComponent("com.bean.ExampleBean")
public class ExampleBean extends UINamingContainer {

    private Integer userId;
    private String name;

    private boolean renderUserId;
    private boolean renderName;

}

【问题讨论】:

标签: java jsf-2 composite-component managed-bean


【解决方案1】:

这里有一个重大的误解。那不是支持豆。那是一个支持组件。

JSF UI 组件实例不是视图范围的,而是请求范围的。它们在渲染响应结束时被销毁(在将它们的状态保存到 JSF 视图状态之后)并在视图构建期间重新创建(并且它们的状态从 JSF 视图状态恢复)。

您已将有状态属性分配为组件的实例变量。这个不对。您应该将它们显式存储在 JSF 状态中。正确的做法是让 getter 和 setter 委托给UIComponent#getStateHelper()。任何被声明为&lt;cc:attribute&gt; 的属性都已经隐式地做到了这一点。您绝对不需要将它们重新声明为支持组件的实例变量。

那些未声明为&lt;cc:attribute&gt; 的布尔值必须重新实现如下:

public Boolean getRenderUserId() {
    return (Boolean) getStateHelper().eval("renderUserId", Boolean.FALSE);
}

public void setRenderUserId(Boolean renderUserId) {
    getStateHelper().put("renderUserId", renderUserId);
}

在您的 action(listener) 方法中,只需相应地调用 setRenderUserId(true)

不要忘记相应地修复 EL 表达式:

#{cc.renderUserId} 

另见:

【讨论】:

  • 效果很好!但这仍然不是我正在寻找的解决方案。我正在寻找一种组件,我可以在其中拥有诸如视图范围之类的属性,并可以更改主题并在客户需要时提交所有信息。如果这个解决方案是请求范围不是我想要的。还是很感谢!
  • 然后只需为数据创建一个普通的后备bean类而不是组件类。
  • 只是一个简短的评论有点离题,但为什么使用 Boolean.false 而不是 false?
【解决方案2】:

您的 FacesComponent 不保持状态,每次调用它时它都是一个新实例。对于您的用例,您似乎应该至少使用具有视图范围的 ManagedBean。这意味着只要您在带有按钮的页面上,实例就会被保留。

【讨论】:

  • 或者,如果在Mojarra 上,您可以考虑升级到2.2,这样您就可以在坚持CDI 的同时使用视图范围。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-11
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2011-11-18
  • 2017-01-19
  • 2012-05-14
相关资源
最近更新 更多