I have two very similar usecases, one works, and another one does not. I have checked the basic things mentioned in other answers (JSF2.1 on JBoss 7.1.1 with PrimeFaces 3.3).
First the sample which works:
<h:form id="processInstanceList">
<p:dataTable id="instances" var="processInstance" value="#{processInstanceList}">
...
<p:column>
<h:commandLink value="#{msg.deleteButtonLabel}"
action="#{runtimeService.deleteProcessInstance(processInstance.id, 'Cancelled by user')}">
</h:commandLink>
with the action methods signature:
void deleteProcessInstance(String processInstanceId, String deleteReason);
and now the sample which does not work:
<h:form id="taskGrouplist">
<p:dataTable id="groupTasks" value="#{groupTaskList}" var="v_task">
...
<p:column>
<h:commandLink value="Übernehmen" action="#{taskList.claimTask(v_task)}"/>
with the action methods signature:
#Named
#RequestScoped
public class TaskList {
public String claimTask(Task task);
on the second sample the task passed to the action method is always NULL. If I just pass an attribute of the task to the method, e.g. with #{taskList.claimTask(v_task.id)} (Task has a getId() method returning a String) and changing the action methods signature to public String claimTask(String id) ... everything I pass in is NULL.
Why doesn't this happen in the first sample? There I pass in two Strings and it works fine...
What is the scope of your RuntimeService bean?
Try to change your TaskList bean scope to ViewScoped or SessionScoped, here is why:
#RequestScoped: A request scoped bean has a lifetime of exactly one HTTP request/response.
So once your dataTable with commandLinks gets rendered (the HTTP request/response is over) you lose all the references for v_task parameters in your action="#{taskList.claimTask(v_task)}" calls.
#ViewScoped: A view scoped bean lives as long as you're interacting with the same JSF view.
Even after the rendering is over, because of the #ViewScope you still have your references on v_task object.
Related
I am trying to execute an action through commandButton inside a dataTable, but the action is not invoked when the commandButton is placed inside the datatable as shown below
<h:form>
<h:dataTable value="#{bean.list}" var="item">
<h:column>
<h:commandButton value="submit" action="#{bean.submit}" />
</h:column>
</h:dataTable>
</h:form>
When I move the commandButton out of dataTable, the action is successfully executed. What is the problem when commandButton is inside datatable? The commandLink has the same problem.
This problem can happen when the list behind #{bean.list} is not exactly the same during the HTTP request of processing the form submit as it was during the request of displaying the form. JSF will namely re-iterate over the list to locate the button pressed and invoke its action.
If the bean is request scoped and the list is not repopulated during bean's (post)construction, or the list's population depends on a request scoped variable which was lost during the form submit, then JSF will retrieve an empty or a completely different list while processing the form submit and thus won't be able to locate the button pressed and won't invoke any action.
The best fix is to put the bean in the view scope and ensuring that you're loading the data model the proper way.
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
private List<Item> list;
#EJB
private ItemService service;
#PostConstruct
public void init() {
list = service.list();
}
// ...
}
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 4
Benefits and pitfalls of #ViewScoped
Is there a way to display a specific JSF page based on the request URL?
Let's say I have a JSF page "details.xhtml". The managed bean "detailsBean" has a list of objects where each object has its own ID. Now if a user requests the page "../details.xhtml?id=1", the list should be queried for an object with ID 1 and the resulting details page of this object should be displayed.
I already wrote a converter implementation class which can convert from object to ID and vice versa, but I don't know how to use it properly. Do I have to work through the JAX-RS specification for this to work or is there a more simple solution?
In JSF you can do this by using a so-called view parameter. You declare these in the metadata section of your Facelet:
<f:metadata>
<f:viewParam name="id" value="#{yourBean.yourObject}" label="id"
converter="yourObjectConverter"
/>
</f:metadata>
This will grab the URL parameter id from the request URL. E.g. if you request the page this appears on with localhost:8080/mypage.jsf?id=1, then 1 will be handed to the yourObjectConverter and whatever this converter returns will be set in yourBean.yourObject.
Your backing bean will thus get the converted object. No need to pollute your backing bean over and over again with the same query code.
#ManagedBean
public class YourBean {
private SomeObject someObject;
public void setYourObject(SomeObject someObject) {
this.someObject = someObject;
}
}
If your backing bean is view scoped, you may want to use the OmniFaces variant of viewParam instead, since otherwise it will needlessly convert after each postback (if your converter does a DB query, you definitely don't want this).
Working full examples:
http://code.google.com/p/javaee6-crud-example/source/browse/WebContent/user_edit.xhtml
http://code.google.com/p/javaee6-crud-example/source/browse/src/backing/UserEdit.java
Further reading:
Communication in JSF 2.0 - Processing GET request parameters
Stateless vs Stateful JSF view parameters
You can achieve this with plain JSF with the following steps
Capture the ID in the request to determine what object is being queried for in your DetailsBean from the request parameter. There are many ways to achieve this, one of which is adding the following annotation to your managed bean (this is currently only permitted for a #RequestScoped bean, see why here).
#ManagedProperty(value="#{param.id}")
int requiredObjectId;
The annotation above will capture the id parameter from the request and assign it to the requiredObjectId variable.
Using the captured Id, setup your object in your bean in a #PostConstruct method
#PostConstruct
public void queryForObject(){
//use the requiredObjectId variable to query and setup the object in the backing bean
}
The object retrieved should be assigned as an instance variable of your managed bean
In your view, you could then reference the queried object that has been setup in the backing bean
<h:panelGrid columns="2">
<h:outputText value="Title"/>
<h:outputText value="#{detailsBean.selectedObject.title}"/>
</h:panelGrid>
If your bean is in a scope broader than the request scope, you'll need a combination of constructs to cleanly pull that request parameter before view rendering.
Capture the request parameter within the JSF view itself using
<f:metadata>
<f:viewParam name="id" value="#{detailsBean.requiredObjectId}" required="true" requiredMessage="You must provide an Object Id"/>
</f:metadata>
**OR**
Due to the nature of JSF Lifecycle processing, doing the above alone may not make the value available for your use in time for object setup. You could use the following instead.
<f:metadata>
<f:event type="preRenderView" listener="#{detailsBean.setObjectId}" />
</f:metadata>
What we've done here is specify a method (that captures the id) in the backing bean that must be executed before the view is rendered, ensuring that the id parameter is available as at the time you need it. Proceed with step 3, only if you're using <f:event/> above.
In the backing bean, you now define the setObjectId method
public void setObjectId(){
Map<String,String> requestParams = FacesContext.getExternalContext().getRequestParameterMap();
requiredObjectId = Integer.parseInt(requestParams.get("id"));
}
Note that the above option is generally a work around/hack and not a clean solution as such
I have a <p:selectManyCheckbox> inside <ui:repeat>, getting it's items from a List of a certain Object Class (provided by <ui:repeat>-variable) and is supposed to save the chosen items into another List of the same Object Class. But it calls the setter method #{cartBean.setSelectedExtras} only for the last entry (last iteration of <ui:repeat>).
<ui:repeat var="item" value="#{category.items}">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
Update:
I changed the above construct just the way BalusC proposed.
Declaration in backing bean is now:
private List<List<Item>> selectedExtras = new ArrayList<List<Item>>();
When I check checkboxes that were created by the first loops of <ui:repeat> and click the <p:commandButton> inside the same <h:form> the setter method of selectedExtras is not called. When I check the checkboxes created in the last loop of <ui:repeat> and click the <p:commandButton> I get an Exception:
javax.el.PropertyNotFoundException: /lightbox-item.xhtml #57,165 value="#{cartBean.selectedExtras[iteration.index]}": null
This construct works fine for me.
As mentioned in among others the showcase page, the omnifaces.SelectItemsConverter uses by default the toString() representation of the complex object as converted item value. So if you didn't override the toString() method (so that it still defaults to com.example.SomeClass#hashcode which changes on every instantiation) and the #{item} managed bean is request scoped, then the list would basically be changing on every HTTP request. This would cause a "Validation Error: Value is not valid" error.
If you add
<p:messages autoUpdate="true" />
or
<p:growl autoUpdate="true" />
so that you get all (missing) validation/conversion messages in the UI, then you should have noticed it.
In order to utilize the omnifaces.SelectItemsConverter at its best, you should override the toString() method accordingly so that it returns a fixed and unique representation of the complex object. E.g.
#Override
public String toString() {
return "Extra[id=" + id + "]";
}
Alternatively, you could put the #{item} managed bean in a broader scope, such as the view scope.
Update as to your update, you're binding the selected values of all checkboxgroups to one and same bean property #{cartBean.selectedExtras}. This way every iteration overrides the property with the values from the current iteration round as long as until you end up with the values of the last iteration. If you've placed a debug breakpoint on the setter, you'd have noticed that.
This is not right. They should each point to a different bean property. Technically, you should have a #{item.selectedExtras} as property. But I think that this makes no sense in your current design. Better would be to make the #{cartBean.selectedExtras} an List<Item[]> or Item[][]. This way you can get them to set based on the iteration index as follows:
<ui:repeat var="item" value="#{category.items}" varStatus="iteration">
<p:selectManyCheckbox id="extraCheckbox" value="#{cartBean.selectedExtras[iteration.index]}" layout="pageDirection" converter="omnifaces.SelectItemsConverter">
<f:selectItems value="#{item.items5}" var="extra" itemLabel="#{extra.name}"/>
</p:selectManyCheckbox>
</ui:repeat>
In case of List<Item[]> you only need to make sure that you preinitialize selectedExtras with nulls as many times as there are #{category.items} in bean's (post)constructor.
for (Item item : category.getItems()) {
selectedExtras.add(null);
}
In case of Item[][], you can suffice with
selectedExtras = new Item[category.getItems().size()];
During some tests made using JSF 2 (mojarra on Glassfish 3.1.1) I've faced with strange behavior I can't explain.
This is my managed bean:
#ManagedBean
#RequestScoped
public class TestBean {
private int id;
public void hideButton() {
id = 0;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
Here is my xhtml page
<h:form>
<h:inputHidden value="#{testBean.id}"/>
<h:outputText value="#{testBean.id}"/>
<h:commandButton value="set 1"
actionListener="#{testBean.setId(1)}">
</h:commandButton>
<h:commandButton value="hide button"
action="#{testBean.hideButton}" rendered="#{testBean.id > 0}">
</h:commandButton>
</h:form>
I expected, the button "hide button" is not visible on initial load of the page and this is really the fact. After a click on the button "set 1", the button "hide button" appeares and that is also to expect.
Really not understandable thing to me is the fact the subsequent click on the button "hide button" does not invoke the method testBean.hideButton and set the id to 0.
I've read the very useful answer from BalusC (thanks a lot really) here
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
and recognize, the problem is related to the attribute "rendered", if I remove it, the action is invoked.
But as far as I aware, the class member must be initialized during the UPDATE MODEL VALUES phase and the condition mentioned in the attribute rendered should be evaluated to true during INVOKE APPLICATION phase and the action should be invoked.
The example works if I change the scope of the bean to View/Session.
But it also works fine if I remove the render attribute from the "hide button"
Would somebody explain such behavior?
In other words, at what phase the expression of the rendered attribute is evaluated to make decision not to invoke the action?
The rendered attribute is also evaluated during apply request values phase, at the moment when JSF needs to identify which action needs to be invoked. If it evaluates false, then the action can't be identified and will thus also not be invoked.
The problem is caused by the too narrow managed bean scope. As your managed bean is request scoped, it get trashed by end of response and recreated (with all properties set to default) on any subsequent request. The model value on which the rendered attribute depends will only be updated during update model values phase, which is too late. You should be placing the managed bean in view scope instead.
Apart from changing the bean scope to view scope, another way is to check the request parameter map value instead in the rendered attribute.
<h:form id="form">
<h:inputHidden id="id" ... />
<h:commandButton ... rendered="#{param['form:id'] gt 0}" />
</h:form>
(by the way your usage of > instead of gt indicates that you're using the deprecated JSP view technology instead of Facelets, I would strongly recommend to migrate to Facelets)
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated
How to choose the right bean scope?
I've found myself the reason of the problem.
The method UIComponentBase.processDecodes (at the Apply Request Values phase) calls isRendered, which returns false, because it is before the Update Model Values. Which skips the decode of the component.
There are some workarounds possible, all of them imho are not cool, but nevertheless it works
It is possible manually to set necessary value in managed bean in (post)constructor from request parameters.
Or to use
I have a page with datatable with product information from which at a product selection action I redirect to product info page passing a parameter:
configurableNavigationHandler.performNavigation("productInfo?faces-redirect=true&prId=" + selectedCpl.getP().getPrId());
In my viewscoped bean in my init method I get the request parameter and fill the objects needed:
#ManagedBean
#ViewScoped
public class ProductInfo implements Serializable {
private Product p;
private Integer prId;
#PostConstruct
private void init() {
HttpServletRequest request = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
if (request.getParameter("prId") != null) {
prId = Integer.parseInt(request.getParameter("prId"));
p = pf.find(prId);
On my productInfo.xhtml I have a product info view and a dialog to edit the product info but when I press submit my the request parameter is null:
<p:commandButton styleClass="button-edit" value="Submit" actionListener="#{productInfo.saveProduct()}" update="prodInfo" oncomplete="dlg.hide();"/>
I'm using jsf 2.0 with primefaces elements.
Can anyone help me? Thank you.
That's not a session parameter. That's a request parameter. That it's null is because you are not sending it along with the submit request. Send it by <f:param>.
<p:commandButton ...>
<f:param name="prId" value="#{productInfo.prId}" />
</p:commandButton>
Unrelated to the concrete problem, there are several other potential problems. First, the view scoped bean should not be recreated when you submit the form. Perhaps you're using tag handlers in the view. Second, you should absolutely avoid hauling the raw javax.servlet API from under the JSF covers as much as possible. Use ExternalContext#getRequestParameterMap() instead. Third, the <f:viewParam> is much cleaner than that postconstruct. Fourth, redirecting by a navigation handler smells like a design problem in the view, e.g. why not use just a GET link?
The in-depth explanations on all of those issues are available in Communication in JSF 2.0.