I am building a JSF application. I defined the GUI and did the select statements query the database using select.
Now I must do the insert statements, but I don't know how to read the value of a JSF input component like <h:inputText> and send it to my bean which performs the insert.
Should <h:inputText> value be mapped through faces-config.xml, so I can have it in my Java code?
You need to put all <h:inputXxx>/<h:selectXxx> components in a <h:form> and bind their value attribute to a bean property via an EL expression like #{bean.property}, backed by a getter/setter pair. When properly set, JSF will automatically set the values in the bean when the form is submitted via a <h:commandXxx> component in the very same form. You can specify a bean action method in action attribute of the <h:commandXxx> component via an EL expression like #{bean.action}, which points to the bare method action(). All submitted values are available right away there the usual Java way.
Given this JSF form example with one input field and one select field:
<h:form>
<h:inputText value="#{bean.text}" required="true" />
<h:selectOneMenu value="#{bean.choice}" required="true">
<f:selectItem itemValue="#{null}" />
<f:selectItem itemValue="One" />
<f:selectItem itemValue="Two" />
<f:selectItem itemValue="Three" />
</h:selectOneMenu>
<h:commandButton value="submit" action="#{bean.submit}" />
<h:messages />
<h:outputText value="#{bean.result}" />
</h:form>
The following bean prints the submitted values to the stdout, proving that JSF has already set the values long before the moment you access it in the action method.
package com.example;
import javax.inject.Named;
import javax.enterprice.context.RequestScoped;
#Named // Use #javax.faces.bean.ManagedBean on outdated environments.
#RequestScoped // Use #javax.faces.bean.RequestScoped on outdated environments.
public class Bean {
private String text;
private String choice;
private String result;
public void submit() {
result = "Submitted values: " + text + ", " + choice;
System.out.println(result);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getChoice() {
return choice;
}
public void setChoice(String choice) {
this.choice = choice;
}
public String getResult() {
return result;
}
}
That's all. Turning the regular form into an ajax form is a matter of nesting a <f:ajax> in the command component as below.
<h:commandButton value="submit" action="#{bean.submit}">
<f:ajax execute="#form" render="#form" />
</h:commandButton>
You can find another example and valuable links at bottom of our JSF wiki page.
Do note that whatever you intend to do with the submitted values is beyond the responsibility of JSF. For example, manipulating it, passing into another class, saving it in database, etc. None of this all is related to JSF. It has as being a HTML form based framework already done its job of providing you the submitted values in flavor of usable Java variables. The remainder is up to you.
To investigate the next step, you should at this point just be doing as if you've already a bunch of prepared / hardcoded variables instead of a whole JSF based user interface. For example, in order save to the values in a database, people usually use a business service layer framework like EJB which in turn uses a persistence layer framework like JPA. Some people even use "plain vanilla" JDBC for that. For more links to concrete examples, start here: JSF Controller, Service and DAO.
Related
I'm using Primefaces in a JSF 2 application. I have a <p:dataTable>, and instead of selecting rows, I want the user to be able to directly execute various actions on individual rows. For that, I have several <p:commandLink>s in the last column.
My problem: how can I pass a row ID to the action started by the command link so that I know which row to act on? I tried using an <f:attribute>:
<p:dataTable value="#{bean.items}" var="item">
...
<p:column>
<p:commandLink actionListener="#{bean.insert}" value="insert">
<f:attribute name="id" value="#{item.id}" />
</p:commandLink>
</p:column>
</p:dataTable>
But it always yields 0 - apparently the row variable f is not available when the attribute is rendered (it works when I use a fixed value).
Anyone has an alternative solution?
As to the cause, the <f:attribute> is specific to the component itself (populated during view build time), not to the iterated row (populated during view render time).
There are several ways to achieve the requirement.
If your servletcontainer supports a minimum of Servlet 3.0 / EL 2.2, then just pass it as an argument of action/listener method of UICommand component or AjaxBehavior tag. E.g.
<h:commandLink action="#{bean.insert(item.id)}" value="insert" />
In combination with:
public void insert(Long id) {
// ...
}
This only requires that the datamodel is preserved for the form submit request. Best is to put the bean in the view scope by #ViewScoped.
You can even pass the entire item object:
<h:commandLink action="#{bean.insert(item)}" value="insert" />
with:
public void insert(Item item) {
// ...
}
On Servlet 2.5 containers, this is also possible if you supply an EL implementation which supports this, like as JBoss EL. For configuration detail, see this answer.
Use <f:param> in UICommand component. It adds a request parameter.
<h:commandLink action="#{bean.insert}" value="insert">
<f:param name="id" value="#{item.id}" />
</h:commandLink>
If your bean is request scoped, let JSF set it by #ManagedProperty
#ManagedProperty(value="#{param.id}")
private Long id; // +setter
Or if your bean has a broader scope or if you want more fine grained validation/conversion, use <f:viewParam> on the target view, see also f:viewParam vs #ManagedProperty:
<f:viewParam name="id" value="#{bean.id}" required="true" />
Either way, this has the advantage that the datamodel doesn't necessarily need to be preserved for the form submit (for the case that your bean is request scoped).
Use <f:setPropertyActionListener> in UICommand component. The advantage is that this removes the need for accessing the request parameter map when the bean has a broader scope than the request scope.
<h:commandLink action="#{bean.insert}" value="insert">
<f:setPropertyActionListener target="#{bean.id}" value="#{item.id}" />
</h:commandLink>
In combination with
private Long id; // +setter
It'll be just available by property id in action method. This only requires that the datamodel is preserved for the form submit request. Best is to put the bean in the view scope by #ViewScoped.
Bind the datatable value to DataModel<E> instead which in turn wraps the items.
<h:dataTable value="#{bean.model}" var="item">
with
private transient DataModel<Item> model;
public DataModel<Item> getModel() {
if (model == null) {
model = new ListDataModel<Item>(items);
}
return model;
}
(making it transient and lazily instantiating it in the getter is mandatory when you're using this on a view or session scoped bean since DataModel doesn't implement Serializable)
Then you'll be able to access the current row by DataModel#getRowData() without passing anything around (JSF determines the row based on the request parameter name of the clicked command link/button).
public void insert() {
Item item = model.getRowData();
Long id = item.getId();
// ...
}
This also requires that the datamodel is preserved for the form submit request. Best is to put the bean in the view scope by #ViewScoped.
Use Application#evaluateExpressionGet() to programmatically evaluate the current #{item}.
public void insert() {
FacesContext context = FacesContext.getCurrentInstance();
Item item = context.getApplication().evaluateExpressionGet(context, "#{item}", Item.class);
Long id = item.getId();
// ...
}
Which way to choose depends on the functional requirements and whether the one or the other offers more advantages for other purposes. I personally would go ahead with #1 or, when you'd like to support servlet 2.5 containers as well, with #2.
In JSF 1.2 this was done by <f:setPropertyActionListener> (within the command component). In JSF 2.0 (EL 2.2 to be precise, thanks to BalusC) it's possible to do it like this: action="${filterList.insert(f.id)}
In my view page:
<p:dataTable ...>
<p:column>
<p:commandLink actionListener="#{inquirySOController.viewDetail}"
process="#this" update=":mainform:dialog_content"
oncomplete="dlg2.show()">
<h:graphicImage library="images" name="view.png"/>
<f:param name="trxNo" value="#{item.map['trxNo']}"/>
</p:commandLink>
</p:column>
</p:dataTable>
backing bean
public void viewDetail(ActionEvent e) {
String trxNo = getFacesContext().getRequestParameterMap().get("trxNo");
for (DTO item : list) {
if (item.get("trxNo").toString().equals(trxNo)) {
System.out.println(trxNo);
setSelectedItem(item);
break;
}
}
}
Thanks to this site by Mkyong, the only solution that actually worked for us to pass a parameter was this
<h:commandLink action="#{user.editAction}">
<f:param name="myId" value="#{param.id}" />
</h:commandLink>
with
public String editAction() {
Map<String,String> params =
FacesContext.getExternalContext().getRequestParameterMap();
String idString = params.get("myId");
long id = Long.parseLong(idString);
...
}
Technically, that you cannot pass to the method itself directly, but to the JSF request parameter map.
I have a ManagedBean that is Session Scoped that injects another two Session Scoped Beans, they both have their proper getter and setters.
My class is as follows:
#ManagedBean(name="oneClass")
#SessionScoped
public class OneClassController implements Serializable {
#ManagedProperty(value="#{myOtherBean}")
public AnotherClass another;
#ManagedProperty(value="#{requestBean}")
public RequestClass request;
public String foo() {
another = getAnotherService(request);
return "page?faces-redirect=true";
}
//getters and setters for AnotherClass and RequestClass
}
Now, the request class holds all the values for a web service request. Those values are filled within a form in a xhtml page.
When the user finishes filling up the request and fires the action from a button, it enters the foo method. The debug shows the request with the correct data, and when I call it, another gets filled up correctly.
Now, page.xhtml looks like this:
<h:outputText value="#{requestBean.agentId}" />
<h:outputText value="#{myOtherBean.name}" />
<h:outputText value="#{myOtherBean.lastname}" />
When page is rendered, all the values from the requestBean are showed correctly, but all the anotherBean values show up empty. Even refreshing the page won't help.
If I trigger a button in page.xhtml to print out in the action method the values from anotherBean:
log.info("Another name: " + another.getName());
they print out fine.
I have in web.xml the saving method set for the server.
By the way, this is not the real naming convention I am using, but right now I'm in another computer without any IDEs nor JDK whatsoever, so I'm trying to replicate the code the best I can.
How can I show the correct values in page?
Try using,
<h:outputText value="#{oneClass.another.name}" />
<h:outputText value="#{oneClass.another.lastname}" />
To view all the variables in sessionScope you can do a:
<h:outputText value="#{sessionScope}" />
This will confirm if "myOtherBean" is in session scope or not.
I have the following piece of code with a simple h:outputText pointing to a int and a p:commandLink to set a value:
<h:form id="f1">
<h:outputText id="text" value="#{testBean.index}"/>
<p:commandLink actionListener="#{testBean.test}" update="text">
<f:setPropertyActionListener target="#{testBean.index}" value="5" />
<h:graphicImage url="/images.png"/>
</p:commandLink>
</h:form>
The managed bean looks like this:
#javax.faces.bean.ManagedBean #ViewScoped
public class TestBean implements Serializable{
private int index; // getter/setter
#PostConstruct public void init() {
index = 0;log.log(Level.WARNING, "#PostConstruct");}
public void test(ActionEvent ae){
log.log(Level.WARNING, "Index: "+index);}
}
The bean is constructed correctly, and after the first click on the image the h:ouputText is updated to 5. But in my log message I only see Index: 0 during the first click on the image.
It's something similar like Jsf updates model value with old value, but I have the JSF #ManagedBean annotation.
Action listeners are invoked in the order they're definied in the view. You want to use action instead of actionListener. Even more, the action should in first place have been used to invoke a business action.
<p:commandLink action="#{testBean.test}" update="text">
<f:setPropertyActionListener target="#{testBean.index}" value="5" />
<h:graphicImage url="/images.png"/>
</p:commandLink>
See also:
Differences between action and actionListener
What is happening is that the test ActionEvent is getting fired before the request values have been applied.
To get a better understanding of the JSF phase lifecycle and when lifecycle events and ActionEvents fire, implement the Debug PhaseListener as specified in the following blog article.
http://balusc.blogspot.com/2006/09/debug-jsf-lifecycle.html
This should help you understand when request values are being applied, and when events are being fired.
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.
I'm getting deeper into JSF 2.0 at the moment and lacking a bit of understanding about the "transport" of managed bean properties from one view to the other. I searched a bit but haven't found a really good example, so if anyone could point me to a tutorial or explain the things a little bit I'd really grateful.
So here is my scenario:
I'm developing a small playground calendar application. The first view select.xhtml contains the calendar selector, where the user can pick a specific date:
<html>
...
<h:form>
<!-- Calendar selector from primefaces -->
<p:calendar value="#{calendarSelect.date}" mode="inline" navigator="true" />
<p:commandButton value="Show entries for date" action="day" />
...
My corresponding backing bean looks like this:
#ManagedBean(name="calendarSelect")
#RequestScoped
public class CalendarSelectComponent {
private Date date = null;
... // Getters and setters
Now when I submit the form from select.xhtml I'm forwarded to day.xhtml
<html>
...
<h:form>
The current day ist:
<h:outputText value="#{calendarEdit.date}">
<f:convertDateTime pattern="dd.MM.yyyy" />
</h:outputText>
The backing bean now looks like this:
#ManagedBean(name="calendarEdit")
#ViewScoped
public class CalendarEditComponent implements Serializable {
private Date date = null;
private CalendarEntryBean currentEntry = null;
private List<CalendarEntryBean> allEntries = null;
....
I am now trying to solve the problem: How do I transfer the date parameter from the selector to the editor?
I've tried a number of options, one was this:
<p:commandButton value="Show entries for date" action="day" />
<f:setPropertyActionListener target="#{calendarEdit.date}" value="#{calendarSelect.date}" />
</p:commandButton>
A debugger shows, that indeed, the date property of the calendarEdit is populated with the value from calendarSelect, but since day.xhtml is a new view, a new CalendarEditComponent backing bean is being created and not the one I've populated with the date from the selector in the select view.
I've read that one solution would be to create a SessionScoped backing bean that does retain all it's values. But this is not the way I think it's supposed to work, because I don't really need the information in the session, I simply want it to "travel" from A to B. Another downside with the session based approach is that I can only use one selector and one editor per session - which I think isn't acceptible if you think of multi window browsing and so on.
I really don't think I'm the first one encountering such a scenario and I'm sure that JSF provides an elegant solution for this but I haven't been able to find that solution.
So once again, if anyone knows how to approach this - I'm listening! ;-)
The <f:setPropertyActionListener> is executed during invoke action phase of the form submit. So it expects that the value is still there at that point. But since your select bean is request scoped, it isn't there during form submit anymore. You want instead to pass a request parameter which get inlined in the output during render response. You can do this with <f:param>.
<p:commandButton value="Show entries for date" action="day" />
<f:param name="date" value="#{calendarSelect.dateAsString}" />
</p:commandButton>
It'll be available as request parameter (note that it only understands Strings, due to the nature of HTTP). You could let JSF set request parameters as managed properties, but since your edit bean is view scoped, this isn't possible with #ManagedProperty. You've got to gather it yourself by ExternalContext.
String dateAsString = externalContext.getRequestParameterMap().get("date");
True, that's clumsy. I would just have used the same bean and view for this and toggle visibility of select/edit forms by rendered attribute. The edit view is after all not directly openable/bookmarkable by a simple GET, isn't it? ;)