【问题标题】:Request scoped page navigation请求范围页面导航
【发布时间】:2012-05-28 08:11:08
【问题描述】:

我正在设计一个使用标准 Java EE 组件(如 JSF 2.0、CDI、EJB 和 JPA)的站点。它处于早期阶段,因此导航非常简单。问题是我在导航时很难保留一些数据。

这是 seminarController 支持 bean:

SeminarController.java

@Named
@RequestScoped
public class SeminarController {
    @Inject
    private SeminarDao seminarDao;

    private int seminarID;
    private Seminar seminar;

    ...
    // Getters and setters
    ...

    public Collection<Seminar> getSeminars() {...}

    public Collection<Seminar> getUpcomingSeminars() {...}

    public void initSeminar() {
        seminar = seminarDao.findSeminar(seminarID);
    }

    public String save() {
        seminarDao.save(seminar);
        return "details";
    }
}

第一页目前只是列出了一些即将举行的并且可能会引起兴趣的研讨会。

index.xhtml

    <ul>
        <ui:repeat value="#{seminarController.upcomingSeminars}" var="seminar">
            <li>
                <h:link value="#{seminar.title}" outcome="details">
                    <f:param name="id" value="#{seminar.seminarID}"/>
                </h:link>
                on
                <h:outputText value="#{seminar.eventDate}"/>
            </li>
        </ui:repeat>
    </ul>

如您所见,我有一个包含链接的列表,如果我单击该链接,我会转到详细信息页面。我正在使用f:param 将研讨会的 id 添加到 HTTP 请求参数中。

详细信息页面(当然)显示了有关即将举行的研讨会的更多详细信息。研讨会也可以通过点击链接进行编辑。

details.xhtml

<f:metadata>
    <f:viewParam name="id" value="#{seminarController.seminarID}"/>
    <f:event type="preRenderView" listener="#{seminarController.initSeminar}"/>
</f:metadata>
<ui:define name="body">

    <h2>#{seminarController.seminar.title}</h2>

    <p>
        <strong>When:</strong>
        #{seminarController.seminar.eventDate}
    </p>
    <p>
        <strong>Where:</strong>
        #{seminarController.seminar.address},
        #{seminarController.seminar.country}
    </p>
    ...
    <p>
        <h:link value="Edit" outcome="edit">
            <f:param name="id" value="#{seminarController.seminarID}"/>
        </h:link> |
        <h:link value="Back to List" outcome="index"/>
    </p>

到目前为止一切顺利,一切正常。

现在我们点击链接编辑研讨会,然后进入编辑页面。我们仍然使用f:param作为当前研讨会的ID。

edit.xhtml

<f:metadata>
    <f:viewParam name="id" value="#{seminarController.seminarID}"/>
    <f:event type="preRenderView" listener="#{seminarController.initSeminar}"/>
</f:metadata>
<ui:define name="body">
    <h:form>
        <h:messages title="Please correct the errors and try again."/>
        <fieldset>
            <h:panelGroup class="editor-label" layout="block">
                <h:outputLabel value="Seminar Title:" for="title"/>
            </h:panelGroup>
            <h:panelGroup class="editor-field" layout="block">
                <h:inputText id="title" value="#{seminarController.seminar.title}"/>
                <h:message for="title" title="*"/>
            </h:panelGroup>
            ...
            <p>
                <h:commandButton value="Save" action="#{seminarController.save}">
                    <f:param id="id" value="#{seminarController.seminar.seminarID}"/>
                </h:commandButton>
            </p>
        </fieldset>
    </h:form>

这是我开始遇到问题的地方。按下保存按钮时,我想返回详细信息页面。详细信息页面应显示当前研讨会以及最新更新的详细信息。问题是当按下保存按钮时,seminarController 中的seminar 对象是null

当呈现编辑视图时,研讨会对象被初始化并用于显示表单中的当前值。但是当按下保存按钮时,研讨会对象不再存在于seminarController 中。并且initSeminar方法也没有被调用。

那么问题是我应该如何设计设计-编辑-设计页面导航?

ViewScope 不起作用,因为我在按保存时导航到新视图。而且无论如何使用 CDI 时 ViewScope 都不存在。

任何帮助将不胜感激!


编辑

Seminar 类有一些在表单中看不到的字段。

Seminar.java

@Entity
public class Seminar {
    @Id
    private int seminarID;
    @Column
    private String title;
    @Column
    private Date eventDate;   // Date when the seminar occurs
    @Column
    private Date createdDate; // Not visible in forms
    ...
    // Some more fields and Getters/Setters
}

编辑2:

我最终在SeminarController

SeminarController.java

@PostConstruct
public void init() {
    if (FacesContext.getCurrentInstance().isPostback()) {
        seminar = new Seminar();
    } else {
        seminar = seminarDao.findSeminar(seminarID);
    }
}

public void setSeminarID(int seminarID) {
    if (this.seminarID != seminarID) {
        seminar = seminarDao.findSeminar(seminarID);
        this.seminarID = seminarID;
    }
}

public String save() {
    seminarDao.save(seminar);
    return "details?faces-redirect=true&includeViewParams=true";
}

然后我将&lt;h:inputHidden value="#{seminarController.seminarID}"/&gt; 添加到edit.xhtml

details.xhtmledit.xhtml 中的&lt;f:event/&gt; 已相应更改,以调用init() 方法而不是旧的initSeminar()

这似乎可行,虽然我不太喜欢 seminar 对象是在回发中使用 new 创建的,然后在设置 seminarID 时被覆盖。

【问题讨论】:

    标签: jsf-2 cdi


    【解决方案1】:

    在更新模型值阶段发生之前,您需要自己准备seminar。预渲染视图事件为时已晚。

    其中一种方法是创建@PostConstruct,在其中检查当前请求是否为回发,如果是,则预先创建Seminar,以便JSF 有机会在更新模型值阶段调用其设置器。

    @PostConstruct
    public void init() {
        if (FacesContext.getCurrentInstance().isPostback()) {
            seminar = new Seminar();
        }
    }
    

    我不确定initSeminar() 做了什么,但我认为您还需要相应地对其进行更改,使其仅在if (!FacesContext.getCurrentInstance().isPostback()) 完成其工作。

    【讨论】:

    • 好的,我明白你的意思了。当我从detail.xhtml 转到edit.xhtml 时,seminar 将使用我的initSeminar 创建,它使用seminarID 和DAO 实例化seminar。当我从edit.xhtml 回到details.xhtml 时,我会在您建议的init 方法中创建seminar。我会试一试,然后再回来。谢谢!
    • 此解决方案的问题是,当我按保存时,研讨会对象不包含从数据库加载的原始研讨会的所有信息。我需要研讨会 ID,以便我可以从数据库中查找研讨会并更改值并保存回数据库。这有意义吗?
    • 使用&lt;h:inputHidden value="#{seminarController.seminar.seminarID}"/&gt;
    • 我已根据您的建议编辑了我的解决方案 (Edit2)。我对seminar 对象在回发期间只是作为“占位符”存在并不完全满意。我可以确定&lt;h:inputHidden/&gt; 在其他输入字段之前被调用吗?
    猜你喜欢
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-09
    • 2018-12-28
    • 1970-01-01
    • 2018-10-08
    • 1970-01-01
    相关资源
    最近更新 更多