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
Related
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;
}
}
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.
I have the following snippet on the home page of my app:
<h:panelGroup rendered="#{loginBean.loggedIn}">
<p>Welcome, #{loginBean.currentUser.firstName}</p>
</h:panelGroup>
LoginBean is #SessionScoped. Since it is being referred to on the home page, an instance of the same will be created when the page loads.
Now, assume that the user never logs in. In that case, my LoginBean is of no use since it won't be holding any information about the user. Wouldn't this be redundant?
Am not saying that this causes problems, but am just wondering about the unnecessary instantiation taking up memory space.
Make the #{loginBean} request/view scoped and manually put the user in the session scope on successful login. The session scope is available as a map by ExternalContext#getSessionMap().
#ManagedBean
#ViewScoped
public class LoginBean {
public void login() {
// ...
if (user != null) {
externalContext.getSessionMap().put("user", user);
}
// ...
}
}
This way you can get away with
<h:panelGroup rendered="#{not empty user}">
<p>Welcome, #{user.firstName}</p>
</h:panelGroup>
See also:
Performing user authentication in Java EE / JSF using j_security_check - the 2nd example (at the bottom)
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?
i have a strange behaviour in my app:
I use a SessionScope bean (Bean A) to hold users preferences. In my other Bean (Bean B), which is in RequestScope, I inject the SessionScope bean.
Bean B has a #PostConstruct method to retrieve a list of values from the database depending on the value in Bean A. The application gets confused when the user changes the value in Bean A and its value in Bean B is not correct at the time #PostConstruct method is invoked. I tested it with logs.
I think all setter methods will be updated before Invoke Application Phase?
Here is a code sample:
Bean A:
#Named
#SessionScoped
public class SessionBean implements Serializable {
private static final long serialVersionUID = -4214048619877179708L;
#Inject private Logger log;
private BankAccount selectedBankAccount;
public BankAccount getSelectedBankAccount() {
return selectedBankAccount;
}
public void setSelectedBankAccount(BankAccount selectedBankAccount) {
log.info("ba: " + selectedBankAccount);
this.selectedBankAccount = selectedBankAccount;
}
Bean B:
#RequestScoped
public class SubAccountListProducer {
#Inject private SessionBean sessionBean;
#Inject private EntityManager em;
#PostConstruct
public void retrieveAllSubAccount() {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<SubAccount> criteria = cb.createQuery(SubAccount.class);
Root<SubAccount> account = criteria.from(SubAccount.class);
log.info("retrieveAllSubAccount: " + sessionBean.getSelectedBankAccount());
criteria.select(account).where(cb.equal(account.get("bankAccount"), sessionBean.getSelectedBankAccount()));
criteria.select(account).orderBy(cb.desc(account.get("name")));
entityList = em.createQuery(criteria).getResultList();
}
Sample logs:
ba: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
retrieveAllSubAccount: BankAccount [accountId=123456789, bankName=Ing DiBa, blz=50010517]
ba: BankAccount [accountId=987654321, bankName=Barclaycard Barclays Bank, blz=20130600]
As you can see... the first two logs are correct... if the user changes preferences (updates the SessionBean), the view will be rerendered with JSF and the last two logs are not in correct order and my app gets confused.
Thank you for help.
The #PostConstruct is not executed during invoke action phase. It is executed directly after bean's construction. The PostConstruct should only be used to preinitialize some stuff depending on injected dependencies directly after bean's construction. Because your bean is request scoped instead of conversation scoped (or view scoped), it will be constructed on every single request.
You need to do the updating/refreshing job in the real action method instead, which is the method you've specified in the <h:commandButton>/<h:commandLink>. E.g.
<h:commandButton value="Submit" action="#{bean.submit}" />
with
public void submit() {
// ...
retrieveAllSubAccount();
}
I also suggest to put your bean in the CDI conversation scope or JSF view scope, so that it don't unnecessarily get reconstructed on every postback to the same view.