当用户返回表单时,如何告诉 Facelets 页面应加载什么对象?

发布于 2024-11-15 22:44:47 字数 1677 浏览 4 评论 0原文

我遇到了一个问题,我解决了,但我觉得我的解决方案是一个糟糕的解决方案。有更好的办法吗?

我有一个页面,在上面放置了表单,它显示了某个对象的属性,如示例所示(省略了明显的细节)。

Ticket.java:TicketController.java

@Entity
public class Ticket {
    @Id
    private Long id;
    private String title;
    private byte priority;
    // Getters, setters...
}

edit.xhtml

@RequestScoped
public class TicketController {
    private Ticket ticket = new Ticket();
    // Getters, setters...

    public String doUpdateTicket() {
        Ticket t = ticketEJB.getTicketById(ticket.getId());
        t.setTitle(ticket.getTitle());
        t.setPriority(ticket.getPriority());
        ticketEJB.updateTicket(t);
        ticket = t;
        return "view.faces";
    }
}

(只是表单,其他一切都是样板文件)

<h:form>
    <h:inputHidden value="#{ticketController.ticket.id}" />
    <h:panelGrid columns="2">
        <h:outputLabel value="ID"/>
        <h:outputLabel value="#{ticketController.ticket.id}"/>
        <h:outputLabel value="Title: "/>
        <h:inputText value="#{ticketController.ticket.title}"/>
        <h:outputLabel value="Priority: "/>
        <h:inputText value="#{ticketController.ticket.priority}" />
        <h:commandButton value="Submit" 
             action="#{ticketController.doUpdateTicket}" />
    </h:panelGrid>
</h:form>

还有TicketEJB,它负责获取这些票证、持久化等。

所以我在表单中创建一个隐藏输入,然后(在托管 bean)我使用提供的 id 找到票证,然后手动将托管 bean 的 ticket 对象中的所有字段复制到获取的票证中,然后保留它......这涉及到违反 DRY 原则(我已经绊倒了当我向 Ticket 添加字段时出现错误,但忘记将其复制到 doUpdateTicket()

,也许有更好的方法来做到这一点?

I had a problem, which I solved, but I feel like my solution is a bad hack. Is there a better way?

I have a page, on which I placed the form, which shows properties of some object, as in example (obvious details omitted).

Ticket.java:

@Entity
public class Ticket {
    @Id
    private Long id;
    private String title;
    private byte priority;
    // Getters, setters...
}

TicketController.java

@RequestScoped
public class TicketController {
    private Ticket ticket = new Ticket();
    // Getters, setters...

    public String doUpdateTicket() {
        Ticket t = ticketEJB.getTicketById(ticket.getId());
        t.setTitle(ticket.getTitle());
        t.setPriority(ticket.getPriority());
        ticketEJB.updateTicket(t);
        ticket = t;
        return "view.faces";
    }
}

edit.xhtml (just the form, everything else is boilerplate)

<h:form>
    <h:inputHidden value="#{ticketController.ticket.id}" />
    <h:panelGrid columns="2">
        <h:outputLabel value="ID"/>
        <h:outputLabel value="#{ticketController.ticket.id}"/>
        <h:outputLabel value="Title: "/>
        <h:inputText value="#{ticketController.ticket.title}"/>
        <h:outputLabel value="Priority: "/>
        <h:inputText value="#{ticketController.ticket.priority}" />
        <h:commandButton value="Submit" 
             action="#{ticketController.doUpdateTicket}" />
    </h:panelGrid>
</h:form>

Also there is TicketEJB, which is responsible for fetching those tickets, persisting, etc.

So I create a hidden input in the form, then (in managed bean) I find ticket, using provided id, then manually copy all the fields from ticket object of managed bean to the fetched ticket, then persist it... It involves the violation of DRY principle (I already stumbled on a bug when I added a field to Ticket, but forgot to copy it in the doUpdateTicket().

So, maybe there is a better way to do this?

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

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

发布评论

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

评论(2

舂唻埖巳落 2024-11-22 22:44:47

只需在视图作用域 bean 的 preRenderView 期间从 EJB 获取原始票证,而不是自己创建一个新票证。假设工单 ID 作为名称为 id 的请求参数传递:

edit.xhtml

<f:metadata>
    <f:viewParam name="id" value="#{ticketController.id}" />
    <f:event type="preRenderView" listener="#{ticketController.preLoad}" />
</f:metadata>
...

TicketController

@ManagedBean
@ViewScoped
public class TicketController {
    private Long id;
    private Ticket ticket;

    @EJB
    private TicketEJB ticketEJB;

    public void preLoad() {
        ticket = ticketEJB.getTicketById(id);
    }

    public String doUpdateTicket() {
        ticketEJB.updateTicket(ticket);
        return "view.faces";
    }

    // ...
}

唯一的区别是输入字段不不要空白。但这不就是“编辑”表单背后的整个想法吗?然后这个问题也可以立即得到解决。

哦,你

<h:outputLabel value="#{ticketController.ticket.id}"/>

真的需要成为

<h:outputText value="#{ticketController.ticket.id}"/>

Just get the original ticket from the EJB during preRenderView of a view scoped bean instead of creating a new one yourself. Assuming that the ticket ID is been passed as a request parameter with name id:

edit.xhtml

<f:metadata>
    <f:viewParam name="id" value="#{ticketController.id}" />
    <f:event type="preRenderView" listener="#{ticketController.preLoad}" />
</f:metadata>
...

TicketController

@ManagedBean
@ViewScoped
public class TicketController {
    private Long id;
    private Ticket ticket;

    @EJB
    private TicketEJB ticketEJB;

    public void preLoad() {
        ticket = ticketEJB.getTicketById(id);
    }

    public String doUpdateTicket() {
        ticketEJB.updateTicket(ticket);
        return "view.faces";
    }

    // ...
}

The only difference is that the input fields don't blank out. But isn't that just the whole idea behind an "edit" form? That issue is then also immediately fixed that way.

Oh and your

<h:outputLabel value="#{ticketController.ticket.id}"/>

really needs to be a

<h:outputText value="#{ticketController.ticket.id}"/>
星軌x 2024-11-22 22:44:47

您可以将票证本身添加为 ManagedBean,但使用 @SessionScoped。这样,票证域对象在请求之间保留其 id,并且 JSF 可以直接更新它。当然,您会失去使用这种方法保持数据短暂的优势,目前您是通过 Request 范围获得的。然后您就绑定到域对象本身展开了​​辩论。

使用 JSF 2,您还拥有视图范围,您可以在其中存储针对 UIViewRoot 的属性,这在您的情况下可能非常需要避免使用隐藏字段,即存储在 viewScope 中具有票证的票证或控制器 - 因此,当用户回发到编辑页面,票证保留在范围内。有些人可能会说您应该在这里使用传输对象来将服务实体与表示层解耦 - 因此更新 TO,将其传递给 EJB 并让 EJB 处理实体的更新和持久性。

或者,您可以仅将长 id 服务器端存储在 @SessionScoped 或 @ViewScoped 中,因为将其存储为隐藏字段可能不安全,因为客户端可以更改它以更新另一个票证。如果您确实使用 Ticket 的另一个实例来捕获 UI 输入,那么您可以在 Ticket 对象上提供复制构造函数,因此 doUpdateTicket 方法本身不包含从一个 Ticket 到另一代码的繁琐复制字段。

为了避免重复,我更喜欢直接绑定到 JPA 实体(AKA 域对象)。我会使用@ViewScoped。

You could add the Ticket as a ManagedBean in its own right but use @SessionScoped. This way the Ticket Domain Object keeps its id between requests and JSF can update it directly. Of course you lose the advantage of keeping data short lived with this approach, which you currently get via the Request scope. And you open a debate about binding to the Domain Object itself.

With JSF 2 you also have the View Scope where you can store attributes against the UIViewRoot, which may be highly desirable in your case to avoid using the hidden fields i.e. store the Ticket or Controller which HAS-A Ticket in viewScope - so while the user postbacks to the edit page the Ticket is kept in scope. Some folk may say you should be using a Transfer Object here to decouple the Service entities from the presentation tier - so update a TO, pass that to the EJB and let the EJB handle the update and persistence of the Entity.

Alternatively you could store just the Long id server side in @SessionScoped or @ViewScoped, as it may be insecure to store this as a hidden field as the client could change it to update another ticket. If you do use another instance of Ticket to capture UI Inputs then you could provide a Copy Constructor on the Ticket object, so the doUpdateTicket method itself does not include the tedious copy fields from one Ticket to another code.

To avoid repetition I would prefer binding directly to the JPA Entity AKA Domain Object. And I would use @ViewScoped.

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