为什么这个ViewScoped托管bean无法正常工作?如何使其工作?

时间:2016-09-28 11:02:32

标签: jsf view-scope

这是我的列表页面:

<h:dataTable value="#{actorSearchBacking.all}" var="actor">
    <h:column>
        <f:facet name="header">
            First Name
        </f:facet>
        #{actor.firstname}
    </h:column>
    <h:column>
        <f:facet name="header">
            Last Name
        </f:facet>
        #{actor.lastname}
    </h:column>
    <h:column>
        <h:form>
            <h:commandButton value="Update Actor" action="pocdetail">
                <f:setPropertyActionListener target="#{actorFormBacking.stupidActor}" value="#{actor}"/>
            </h:commandButton>
        </h:form>
    </h:column>
</h:dataTable>

在我的本地环境中看起来像这样:

Actor List Page

这是 pocdetail.xhtml ,这是Update Actor按钮的操作:

<h:body>
    <h:form id="updateActorForm"
            prependId="false">
        <h:inputText id="firstname" value="#{actorFormBacking.stupidActor.firstname}"/>
        <h:inputText id="lastname" value="#{actorFormBacking.stupidActor.lastname}"/>
        <h:commandButton id="updateActorButton"
                         value="Update Actor!"
                         action="#{actorFormBacking.updateActor()}"/>
    </h:form>
</h:body>

最后 ActorFormBacking 如下:

@ManagedBean
@ViewScoped
public class ActorFormBacking implements Serializable {

    private Actor stupidActor;

    public Actor getStupidActor() {
        return stupidActor;
    }

    public void setStupidActor(Actor stupidActor) {
        this.stupidActor = stupidActor;
    }
}

当我调试应用程序时,我看到调用了setStupidActor并且设置了属性stupidActor,但是当调用getter时,它再次为null。

由于这是一个ViewScoped bean,我希望stupidActor不为null,我希望看到pocdetail.xhtml页面用值填充,但我看到的只是空输入文本,因为stupidActor为null。

我错过了什么?为什么再次创建ViewScoped bean并且属性为null?

顺便说一句,我正在使用软件包中的注释:

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

1 个答案:

答案 0 :(得分:3)

您似乎正在从一个视图导航到另一个视图。换句话说,您将销毁当前视图并创建新视图。逻辑上,视图范围也将被销毁并新创建,包括所有视图范围的托管bean。两个视图恰好引用了视图作用域托管bean,但这并未改变此行为。

视图范围的bean与视图本身一样长。就像请求作用域bean一样,只要请求本身就存在,等等。为了更好地理解JSF(和CDI)中各种范围的生命周期,请转到此Q&amp; A:How to choose the right bean scope?

然而,理解功能要求。您需要单独的主 - 详细信息页面,并将所选项目从母版页传递到详细信息页面进行编辑。有几种方法可以实现这一目标:

  1. 规范的方法是使用可收藏的GET链接而不是不可指定的POST链接。替换下面的部分

    <h:form>
        <h:commandButton value="Update Actor" action="pocdetail">
            <f:setPropertyActionListener target="#{actorFormBacking.stupidActor}" value="#{actor}"/>
        </h:commandButton>
    </h:form>
    

    由此

    <h:link value="Update Actor" outcome="pocdetail">
        <f:param name="stupidActor" value="#{actor.id}" />
    </h:link>
    

    并在详细信息页面中,通过其标识符获取Actor,该标识符作为查询字符串参数传入。这在Q&amp; A:Creating master-detail pages for entities, how to link them and which bean scope to choose中有详细说明。 @FacesConverter(forClass)在这里非常有用。

  2. 如果您因某些原因想要坚持使用POST,那么最好的办法是将其存储在请求范围内。

    FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("stupidActor", stupidActor);
    

    并在同一个bean的@PostConstruct中检索它

    stupidActor = (Actor) FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("stupidActor");
    
  3. 如果您碰巧使用CDI,或者对我开放(我强烈建议,JSF 2.3.0-m06中已经弃用了JSF托管bean,另请参阅Backing beans (@ManagedBean) or CDI Beans (@Named)?),然后考虑使用MyFaces CODI的@ViewAccessScoped。只要所有postbacked视图显式引用bean,具有此范围的Bean就会存在。一旦您使用GET导航,或者当导航视图没有引用该bean时,它就会被销毁。

    @Named
    @ViewAccessScoped
    public class ActorFormBacking implements Serializable {}
    
  4. 将两个视图合并到具有条件渲染的主 - 细节部分的单个视图中。你可以在这个Q&amp; A中找到一个启动示例:Recommended JSF 2.0 CRUD frameworks。或者,如果您碰巧使用PrimeFaces,How to show details of current row from p:dataTable in a p:dialog and update after save