RichFaces extendedDataTable: prevent model reloading during 'onselectionchange' event - jsf-2

I have an "extendedDataTable" like this:
<h:form>
<rich:extendedDataTable id="someTbl"
value="#{someBean.allData}"
var="dataItem"
selection="#{dataSelectionListener.selection}"
selectionMode="single">
<a4j:ajax execute="#form"
render="#none"
event="selectionchange"
listener="#{dataSelectionListener.onSelectionChange}"/>
<rich:column>
<h:outputText value="#{dataItem.name}"/>
</rich:column>
</rich:extendedDataTable>
</h:form>
and managed bean:
#Named
#RequestScoped
public class SomeBean {
#Inject
private SomeService someService;
public List<DataItem> getAllData() {
// Getting data from DB
return someService.getAllData();
}
}
dataSelectionListener is also managed bean with scope "session".
Every time I select some row in my table method getAllData is called twice (first before calling of the method onSelectionChange and thereafter). It causes two unwanted queries to DB.
What is the right way to resolve this issue?
Sorry if my question is somewhat silly and thanks in advance.

I think that your problem is related to #RequestScoped annotation. Request scope means that the object is defined as an attribute attached to the HttpRequest object and shares its lifetime. The HttpRequest object is created when the application container accepts an HTTP request from the client. It is effectively destroyed (goes out of scope) when the container has finished transmitting the response to that request. When a new HTTP request comes in, a new request-scope object is created. So anything that you want to exist between HTTP requests has to be placed in a longer-lived scope (such as session scope), or the data has to to be sent back as part of the response in such a way that it will be sent back as part of the next request.

After short research (thanks to the old article in BalusC's blog) I found out that double call of the method that provides data for the table is okay in JSF world (first time getter is called during phase APPLY_REQUEST_VALUES and second - during phase RENDER_RESPONSE). So I really shouldn't use bean with scope "request". As I understood the best scope for this case is "view" in conjunction with a lazy data loading in the getter:
#ManagedBean
#ViewScoped
public class SomeBean {
#Inject
private SomeService someService;
private List<DataItem> allData;
public List<DataItem> getAllData() {
if (allData == null)
// Getting data from DB
allData = someService.getAllData();
return allData;
}
}

Related

Same #PostConstruct calling twice, data not surviving

Hi I'm learning JSF/Primefaces. whenever user click on a link it should forward to the edit page with the information, but it's empty.
I tried to debug, following flow happens:
user click the link of the element that wants to edit
QuoteStatusList.init() is called
QuoteStatusForm.init() is called
QuoteStatusForm.edit is called
QuotestatusForm.quoteStatus bean is filled with the information
return "edit"
foward to quoteStatusForm.xhtml
and QuoteStatusForm.init() is called again, all datas filled are lost
I found this but I'm now only using jsf annotation to manage view beans
QuoteStatusList.java
#ManagedBean
#RequestScope
public class QuoteStatusList extends BasePage implements Serializable {
#PostConstruct
public void init(){
log.debug("initing...");
}
...
}
QuoteStatusForm.java
#ManagedBean
#ViewScope
public class QuoteStatusForm extends BasePage implements Serializable {
#PostConstruct
public void init(){
log.debug("initing...");
}
public String edit() {
log.debug("editing..");
if (idQuoteStatus != null && idQuoteStatus != 0) {
quoteStatus = quoteStatusManager.get(idQuoteStatus);
} else {
quoteStatus = new QuoteStatus();
}
return "edit";
}
}
BasePage.java
#ManagedBean
#RequestScoped
public class BasePage {
//nothing is injected
//no other #postConstruct function
}
QuoteStatusList.xhtml
<h:commandLink action="#{quoteStatusForm.edit}" value="#{quoteStatus.idQuoteStatus}">
<f:param name="idQuoteStatus" value="#{quoteStatus.idQuoteStatus}"/>
</h:commandLink>
faces-config.xml
<navigation-rule>
<from-view-id>/quoteStatusList.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/quoteStatusForm.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<from-view-id>/quoteStatusForm.xhtml</from-view-id>
<navigation-case>
<from-outcome>edit</from-outcome>
<to-view-id>/quoteStatusForm.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
What you're experiencing is appropriate behaviour for #RequestScoped and #ViewScoped beans.
#RequestScoped - Beans of this scope will not survive a redirect/forward to another page. That means that if you're on a page backed by a bean of this scope, whenever you issue a new HTTP request (either ajax, a full-on page refresh, or a redirect), the instance of that bean you're working on is destroyed, voided, ceases to exist. Along with all its member variables
#ViewScoped - Beans of this scope will also not survive a full redirect/forward. They will however survive page refreshes and ajax. What this means is that, as long as you stay on the same page (backed by a #ViewScoped bean), don't return any navigation case, you're guaranteed to be working with the same instance of the bean.
How are these beans supposed to communicate then? If leaving one page means that you lose everything the backing bean contains, what your options (you should be asking)? Well, there are a number of ways that JSF beans can communicate. Read through the gospel on inter-bean communication
So what's happening in your case
QuoteStatusList list is destroyed when you navigate away from the page that it backs. This also means that when you come back, you're dealing with a brand new instance of that bean (and that's why init is called twice)
QuoteStatusForm was destroyed because you returned edit from that bean, causing the instance you're working with to be destroyed and recreated on page load
What to do:
To avoid destroying QuoteStatusForm, you can just return null from edit

<h:link> disabled attribute not evaluating properly

I have an #Stateful EJB in a #SessionScoped bean.
My EJB:
#Stateful
public class SomeEjb implements someEjbInterface{
private SomeEntity entity;
#Override
public Boolean getEntityAssigned(){
return entity!= null;
}
#Override
public void selectEntity(String id){
//assign entity with some values retrieved from db according to the criteria
}
}
My Session Scoped Bean:
#ManagedBean
#SessionScoped
public class SessionBean{
#EJB
private SomeEntity entity;
//getter and setter
public String selectEntity(){
entity.selectEntity(someId);
return null;
//Edited: if using this, no problem will occur.
// return "index";
}
}
My Page index.xhtml (xmlns omitted):
<h:form>
<h:commandButton value="Select entity" action="#{sessionBean.selectEntity()}">
</h:form>
<h:link outcome="someOutcome" disabled="#{sessionBean.entity.entityAssigned}">
I expect that the link is initially disabled, when I click "Select entity", the ejb will retrieve an entity from database, if the retrieve succeed, then the link will be enabled.
The problem is that when I click the button, the link will break (rendering an tag with href attribute but no innerHtml to click). It can only be fixed if I reload the page without data resubmission(re-enter the page by pressing enter at the url, not using F5 which will resubmit the form).
The error message is:
HTML nesting warning on closing span: element a rendered by component : {Component-Path : some long component path refer to the link element} not explicitly closed
Does anyone know what did I messed up with the rendering?
Edit:
I just found out that the problem does not exist if I return the outcome of that same page instead of null, which probably discard #ViewScoped bean I used to call sessionBean.selectEntity(). Can anyone explain the mechanism for the cause of this difference?
The documentation says that the "toString" of the object returned by the method will be used to handel navigation, you can try returning "" (no navigation, only refresh the page).
Please add the relevant parts of the xhtml.

jsf2.0 - How to get the values in other jsf page's bean in request scope

I have two pages myaccount.xhtml and selectbank.xhtml
In my account page there is one option for recharge account in which user will enter the amount when user will press submit button then it will goto the select bank page using following bean method.
public String gotoPayMethod() {
FacesMessage doneMessage=new FacesMessage("Redirecting to Payment Type Page");
FacesContext.getCurrentInstance().addMessage(null, doneMessage);
return "SelectBank";
}
When user will goto to selectbank there user will have to submit payment method but in this page it shows the amount as null which was entered in the previous page.
Both the pages are using the same bean and the scope of the bean is request scope.
So how can I access that value without passing this values through URL GET method.
Just for my satisfaction I used session scope then it was working but I know thats not the proper way because I start using session scope for each pages then it will not be efficient.
Thanks
Well, if your beans are RequestScoped than you don't have same bean for both pages. These beans are recreated for every request, so you should pass parameters. Change return statement of your gotoPayMethod() to:
return "SelectBank?faces-redirect=true&includeViewParams=true";
and on selectbank.xhtml add:
<f:metadata>
<f:viewParam name="amount" value="#{bean.amount}" />
</f:metadata>
Adapt this to your property and bean name.
If using parameters is not a solution you can add this parameter in the session, and remove it from session in second bean when you retrieve it:
FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put("amount", amount);
((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("amount");
Second construction for removing the attribute is necessary as Map returned from getSessionMap() is immutable.
You can use the #{flash} object that will keep your data until the next view. This way you won't need to deal with view parameters.
Details from myaccount.xhtml:
<h:form>
<h:outputText value="Enter amount: " />
<h:inputText value="#{flash.amount}" />
<br/>
<h:commandButton value="Go to payment method" action="#{bean.gotoPayMethod}" />
<h:form>
Bean of both views:
#ManagedBean
#RequestScoped
public class Bean {
#ManagedProperty("#{flash}")
private Flash flash;
private int amount = -1;
public Bean () { }
public String getAmount() {
if(amount == -1) {
int val = Integer.parseInt((String)flash.get("amount"));
flash.keep("amount");
amount = val;
}
return amount;
}
public Flash getFlash() {
return flash;
}
public void setFlash(Flash flash) {
this.flash = flash;
}
public String gotoPayMethod() {
//do business job
return "SelectBank?faces-redirect=true";
}
}
Details from selectbank.xhtml:
<h:outputText value="Amount entered by user is #{bean.amount}" />
Your use case is not of simple request/response cycle, the life span is more than one request response which makes it candidate for session scope.
Using hidden variable or GET parameters in URL is not good practice especially for a banking application. Where security is so important dont compromise on small memory foot print.
If flash scope map simplifies the case you can use it, but I would not go for such a thing.
Update: Forgot to mention you can check Conversation scope too.

JSF 2.0 Setting a variable in the session scoped before a view is shown?

I would like to apply a variable in the session scoped using before the view is displayed, than this view will use this variable.
Here is the link:
<h:link value="#{msg.persondeactivate}" outcome="persondeactivate" />
Here is the faces-config.xml
<navigation-rule>
<navigation-case>
<from-outcome>persondeactivate</from-outcome>
<to-view-id>/deactivatePerson.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
Here is the view (deactivatePerson.xhtml):
...<h:outputText value="#{msg.personIsDeactivate}" rendered="#{controller.personDeactivated}" style="color:green" />... <h:commandButton action="#{controller.deaktivieren}" value="#{msg.deactivate}"></h:commandButton>...
Here is the managed bean:
#ManagedBean #SessionScoped public class Controller { ... private boolean personDeactivated = false; public String deaktivieren(){ personDeactivated = false;
// Deactivate process personDeactivated = true; return "persondeactivate";} ... }
I want that the variable personDeactivated is set to false before the view (deactivatePerson.xhtml) for the second time by is called.
It does not work.
Can someone please tell me what is wrong?
Thanks in advance.
You can use <f:event type="preRenderView"> to invoke a backing bean listener method before the view is rendered.
<f:event type="preRenderView" listener="#{controller.onPreRenderView}" />
with
public void onPreRenderView() {
if (!FacesContext.getCurrentInstance().isPostback()) {
// Do your job here when the view is been freshly requested by GET.
}
else {
// Do your job here when a POST request is been performed by command link/button.
}
}
Unrelated to the concrete problem, I have the impression that the Controller is actually in the wrong scope. A much better solution would be to make it #ViewScoped instead. This way the bean instance will be freshly created on every new GET request and live as long as you're POSTbacking to the very same view (and thus you won't encounter inconsitenties and unintuitive behaviour when having the same page open in multiple browser tabs/windows in the same session which would share the very same session scoped bean!). See also How to choose the right bean scope?

Managed bean property not maintained in the next page by #ManagedProperty

I am new for JSF. In my project am using #ManagedBean, #RequestScoped. Am using 4 pages in my project. My problem was bean values not maintain in the second, third and fourth pages. Am using getter and setter properly. If i not use #ManagedProperty the bean value maintained properly. But i need to use the
#ManagedProperty. Could you please advise me how to solve this issue. I have copied some sample code for reference.
#ManagedBean
#RequestScoped
public class ArticlePrepToolManagedBean implements Runnable, Serializable {
#ManagedProperty (value="#{param.jidName}")
private String jidName;
#ManagedProperty (value="#{param.aidName}")
private String aidName;
private List<com.elsevier.ArticlePrepTool.db.ItemZipContains> usabilityDetailList = null;
public String getAidName() {
return aidName;
}
public void setAidName(String aidName) {
this.aidName = aidName;
}
public String getJidName() {
return jidName;
}
public void setJidName(String jidName) {
this.jidName = jidName;
}
public List<ItemZipContains> getUsabilityDetailList() {
return usabilityDetailList;
}
public void setUsabilityDetailList(List<ItemZipContains> usabilityDetailList) {
ArticlePrepToolManagedBean.usabilityDetailList = usabilityDetailList;
}
}
My project url is (http://localhost:8080/articlepreptool/) but input for my project is jidName=AEA aidName=10663. that input given by some other webpage that is if user trigger using the following href "PrepTool". Depends on the input i fetched some data in my project DB (using JPA) and list out the data in the first page. But if i goes to next page all previous data stored in that list which i got from DB was cleared that is all list values and variables which set in the bean becomes null. So could you please advise me how to solve this issue.That problem occured only if i used the #ManagedProperty. I used #ManagedProperty to fetch the input values comes through url, because the input values of my project comes through other web page.
A #ManagedProperty("#{param.foo}") basically sets the HTTP request parameter with name "foo" as a bean property directly after bean's construction. If you're retrieving null values for them, then it simply means that those parameters are not present in the HTTP request.
Assuming that you're navigating by a plain link, then you need to fix your links to include the request parameters:
<h:link value="Go to page2" outcome="page2">
<f:param name="jidName" value="#{bean.jidName}" />
<f:param name="aidName" value="#{bean.aidName}" />
</h:link>
This will result in something like:
<a href="page2.xhtml?jidName=foo&aidname=bar">
This way those parameters can be set as bean properties.
Alternatively, instead of #ManagedProperty you could also use <f:viewParam> on all pages and add includeViewParams=true to the outcome. See also ViewParam vs #ManagedProperty(value = "#{param.id}")
If you're navigating by a form submit, then there's really no reason to use them. Or you must be abusing forms instead of links for plain vanilla page-to-page navigation.

Resources