这是我的列表页面:
<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>
在我的本地环境中看起来像这样:
这是 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;
答案 0 :(得分:3)
您似乎正在从一个视图导航到另一个视图。换句话说,您将销毁当前视图并创建新视图。逻辑上,视图范围也将被销毁并新创建,包括所有视图范围的托管bean。两个视图恰好引用了视图作用域托管bean,但这并未改变此行为。
视图范围的bean与视图本身一样长。就像请求作用域bean一样,只要请求本身就存在,等等。为了更好地理解JSF(和CDI)中各种范围的生命周期,请转到此Q&amp; A:How to choose the right bean scope?
然而,理解功能要求。您需要单独的主 - 详细信息页面,并将所选项目从母版页传递到详细信息页面进行编辑。有几种方法可以实现这一目标:
规范的方法是使用可收藏的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)
在这里非常有用。
如果您因某些原因想要坚持使用POST,那么最好的办法是将其存储在请求范围内。
FacesContext.getCurrentInstance().getExternalContext().getRequestMap().put("stupidActor", stupidActor);
并在同一个bean的@PostConstruct
中检索它
stupidActor = (Actor) FacesContext.getCurrentInstance().getExternalContext().getRequestMap().get("stupidActor");
如果您碰巧使用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 {}
将两个视图合并到具有条件渲染的主 - 细节部分的单个视图中。你可以在这个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。