JSF PrimeFaces ViewScope without calling init() - jsf-2

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.

Related

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";
}
...

JSF 2 #PostConstruct method called twice

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.

CDI bean List in datatable is null on submit from JSF

Please note: This question is about CDI scopes as we are using CDI scopes in the app and not JSF scopes.
1) Controller Bean (TestController.java) which is in RequestScoped (enterprise context) is called index_cut.xhtml, when we come for first time on this page.
2) On button “Load”, we load the following method to populate the sapFinancialPeriodList which works fine and displays the data
3) After changing the content on the page and submitting, the sapFinancialPeriodList appears as NULL in the following method –
Any suggestions?
Your bean is request scoped and you're loading the data model on action only instead of on (post)construction. When the HTTP response after the action which loaded the data is finished, then the bean is garbaged. The subsequent request would get a brand new instance of the bean with all properties set to default. However, as the same data model isn't been preserved during (post)construct, it remains empty.
In JSF2 you'd solve this with using #ViewScoped. This way the bean will live as long as you're interacting with the same view by postbacks (which return null or void).
In CDI you'd need to solve this using #ConversationScoped, which in turn requires some additional #Inject Conversation boilerplate, complete with begin() and end() calls at the right moments. For a concrete example, see also What scope to use in JSF 2.0 for Wizard pattern?.
An alternative is to pass the parameters responsible for creating the data model to the subsequent request via <f:param> in the command link/button as follows
<h:commandButton value="save" ...>
<f:param name="period" value="#{bean.period}" />
</h:commandButton>
and then recreate exactly the same data model in (post)constructor of the request scoped bean as follows
String period = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get("period");
List<SapFinancialPeriod> sapFinancialPeriodList = someservice.list(period);
(the above is by the way nicer to solve with #ManagedProperty if you were using standard JSF; as far as I know CDI doesn't have an annotation which enables you to set a HTTP request parameter as a bean property)
See also:
How to choose the right bean scope?
Unrelated to the concrete problem, the upcoming JSF 2.2 solves this functional requirement in a nicer way using the new "Faces Flow" feature with the new #FlowScoped annotation and the new xmlns:j="http://java.sun.com/jsf/flow" tags.

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";

viewscoped bean doesn't save session parameter

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.

Resources