JSF 2 #PostConstruct method called twice - jsf-2

I've got a strange behavior: my method annotated with #PostConstruct is called twice.
Debugging it, I saw that my search page called it before the command link's action methos mbean.edit was called. My bean MBeanSearch is request scoped, my MBean is view scoped.
My view search.xhtml:
<h:commandLink value="#{var.value}" action="#{mbean.edit}">
<f:param name="id" value="#{var.id}"/>
</h:commandLink>
I've also got a target view var.xhtml.
Relevant extract from my MBean bean:
public String edit() {
return "/pages/var.xhtml";
}
#PostConstruct
public void initialize() { }
With this code, my #PostConstructis called after my edit method and later it is called again.
I think that I'm using the #PostConstruct in a wrong way (I think MBean needs to be up before any method). But what is the alternative to edit an object in a page different from the search page?

The problem seems to be that the view scoped managed bean mbean (I think, it is a bit unclear) is used in search.xhtml and var.xhtml.
When you call the action method your are still on view search.xhtml. You get a bean instance bound to view scope for this view and the first call to the #PostConstruct method.
The action method returns the view ID of the second page var.xhtml and JSF navigates to this page. If you use mbean in this page too, you get a new instance of the bean as the view changed. This explains the second call to the #PostConstruct method.

Related

h:commandLink inside h:dataTable not invoked [duplicate]

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

JSF PrimeFaces ViewScope without calling init()

I have encounter an issue using Primefaces dataTable that is the jsf will call my managed bean init() which will do a search from database.
In my jsf, user are able to perform another search which pass an argument in String.
<p:commandButton id="btnChild" type="Submit" icon="ui-icon-plus" styleClass="action-buttons" value="Child" update=":frmSysMenu:messages dtSysMenu" ajax="false" action="#{Controller.doParent('C')}" process="dtSysMenu" />
my managed bean are able to execute the doParent(String) and will go back to the search screen again but once my screen is loaded, it will call init() which:
#PostConstruct
#Override
public void init(){
try {
doSearch(nodeValue,"root");
setParentNode(nodeValue);
} catch (Exception e) {
addErrorMessage(e.getMessage());
e.printStackTrace();
Messages.addGlobalError(e.getMessage());
LOG.error(e.toString(), e);
}
}
and it will gone back to the first query.
is there any other way that I can do?
Does RequestScoped help?
From the specs (or somewhere else, OP did not mention it)
#ViewScoped
A bean in this scope lives as long as you're interacting with the same JSF view in the browser window/tab. It get
created upon a HTTP request and get destroyed once you postback to a
different view. It doesn't immediately get destroyed when you
leave/close the view by a GET request, but it is not accessible the
usual way anymore. JSF stores the bean in the UIViewRoot#getViewMap()
with the managed bean name as key, which is in turn stored in the
session. You need to return null or void from action (listener)
methods to keep the bean alive. Use this scope for more complex forms
which use ajax, data tables and/or several rendered/disabled
attributes whose state needs to be retained in the subsequent requests
within the same browser window/tab (view).
in the function I just change to void and jsf won't call the init() again.

How to trigger destruction of viewscoped bean?

I have a #ViewScoped-annotated managedbean whose #PostContruct-method fetches a list from database to be displayed in a table in the view.
Now when I delete an item I want the changes to be seen in the view.
To keep this dynamic and reusable I only want to delete from database (not manually from list). So I need to destroy/recreate the bean I suppose. Now I do this by navigating to the same view. But the way I do is not reusable.
Can I just destroy the bean manually or navigate to the same view without explicitly navigating to THAT specific view (reusability)?
I am using JSF 2.1
You're already on the right track. viewMap is just like any other map; You can remove a ViewScoped bean by name. Please excuse the atrocious chaining:
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove("yourBean");
One solution I found is to destroy the bean by
FacesContext.getCurrentInstance().getViewRoot().getViewMap().clear();
I am not sure if this is the way to go because it simply destroys every viewscoped bean. It's not that bad in this case, but doesn't feel clean.
I appreciate any thought on that or alternative solutions.
When you return a non null value inside a method from an action attribute the Bean gets recreated.
index.xhtml
...
<h:commandButton value="delete" action="bean.delete" />
...
Bean.class
...
public String delete() {
// do operations
return "index.xhtml?faces-redirect=true";
}
...

JSF2: commandLink action method not always called

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.

Replacing a managed bean instance from the backing bean

Doing a CRUD, I have a RequestScoped Ticket bean.
I have an XHTML page that until now I have been using to create the new Tickets; the ticket fields are populated directly (#{ticket.description} and so on)
Now I have a search form that lists the tickets, and with each ticket a link with the ticket id as a parameter. I want the backing bean to retrieve the ticket bean from EJB/JPA (already done) and put it into the request. I see 3 ways to do so:
Copy the data from the bean retrieved from JPA into the bean provided by injection. Ugly / prone to omissions.
Use ExternalContex#getRequestMap and put the bean there myself. Does not look very proper. Am I right?
Include my ticket bean into another bean class so I can do myNewBean.setTicket(ticketFromJpa);. Seems the best of the options, yet I do not like having to prefix all my EL in the page just for this.
There is out there any cleaner, more proper way of doing what I want?
Thanks in advance.
UPDATE:
To reword what I want, with a little more information. I have a commandLink in page P1, that calls action A in backing bean B1. B1 does its logic and redirects to page P2.
I wanted to set a (request scoped) bean B2 in action A, and that B2 would be available to draw P2.
From experiments, I have found that after leaving action A the framework creates a new B2 request scoped bean, so it looks like that the request scope is shorter than I expected.
The flash scope propesed by Damian looks like it works more like I want, but forces me to redesign the page around #{flash} and that (when I want to use it to create a new bean) I must also add the bean to the flash in an action method (currently it justs goes to the page and the managed bean is available)
I expected a Request scoped bean to be maintained since
To view the ticket in another page, you could do one of the following:
1) use h:link with a f:param containing the ID of the ticket
<h:link value="#{ticket.description}" outcome="/viewTicket" >
<f:param name="id" value="#{ticket.id}" /
</h:link>
Then in the viewTicket.xhtml (or whatever you called the page) you read the ID parameter, and get the ticket from JPA.
This is how the managed would basically look like:
#ManagedBean
#ViewScoped
public class ViewTicketMBean implements Serializable {
private String ticketId;
private Ticket ticket;
#PostConstruct
public void init() {
ticketId = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("id");
// read ticket from JPA by ID
}
}
2) instead of h:link, if you don't want to expose ticket ID, you can use a commandLink, which before navigating to viewTicket.xhtml, gets the ticket from JPA and puts it in flash scope. Then, in viewTicket you get the ticket from the flash scope. This is how the action method of the commandLink couldl look like
Ticket ticket = null;
// get ticket from JPA
FacesContext.getCurrentInstance().getExternalContext().getFlash().put("ticket", ticket);
return "/viewTicket";

Resources