How can I tell the Facelets page, what object it shall load, when the user returns the form?
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?
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}"/>
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.
精彩评论