Bean initialization JSF (Primefaces) - jsf

when is a bean initialized? I used #RequestScoped managed bean annotation.
I have a page - products.xhtml and I am redirecting on the same page depending on the version a clicks.
For example, user clicks 2019.000, it redirects to the same page~
/products.xhtml?faces-redirect=true&ver=2019.000
My problem is I have a dialog that opens when a user clicks on a p:commandLink. The bean needed to populate the dialog's contents is DownloadView.java::getRoot().
Here's a snippet of my DownloadView.java.
#ManagedBean(name = "DownloadView")
#ViewScoped
public class DownloadView implements Serializable {
private TreeNode root;
private String path;
public TreeNode getRoot() {
DownloadBI downloadBI = new DownloadBI(modifyPath(this.path));
this.root = downloadBI.getFilesTreeNode();
if (root.getChildCount() != 0)
this.root.getChildren().get(0).setExpanded(true);
return this.root;
}
. . .
The default selection when page loads is 2020.000. The dialog works perfectly fine with the correct target links when loaded from the default selection 2020.000. But, when I redirect to 2019.000 and try to open a dialog, it seems that the bean retained the data from 2020.000.
Here is a snippet of my p:commmandLink calling the dialog from products.xhtml.
<p:commandLink value="#{startup.name}" title="#{startup.name}"
process="#form" update=":download-form:downloadPanel"
oncomplete="PF('downloadDialog').show()" >
<f:setPropertyActionListener target="#{DownloadView.path}" value="#{startup.url}"/>
</p:commandLink>
I was assuming that the value of startup.url should change every time the commandlink is clicked. It does but, its the same value from 2020.000. While testing its behavior, I noticed that the form id is the same from 2020.000 and 2019.000.
From chrome source:
j_idt161:0:j_idt163:1:download-view
is the same when selecting from 2019.000. They should be different since they have different urls.
I thought that this is scoping issue, but I tried all scopes. But, it is still not working.
I am thinking that I should create my DownloadView.java bean only when the commandLink is clicked, and when the dialog is closed, it should also be destroyed. But, I am not sure how to do this as well. :(
I have been debugging this for days now and I'm stuck. I hope someone could give some ideas how to resolve this. I am new to JSF/Primefaces, by the way. Please help!

Related

JSF / EL evaluates onClick during rendering of page. Why? [duplicate]

This question already has an answer here:
How to call JSF backing bean method only when onclick/oncomplete/on... event occurs and not on page load
(1 answer)
Closed 6 years ago.
Recently I ran into a problem with one of my . I have a separate xhtml containing conditionally rendered icons/links to show different kinds of popups. This xhtml is basically a container for specific kinds of popups that I can include on different pages. The rendered conditions (and a passed ui:parameter) make sure only the relevant icons/links get shown depending on where this xhtml is included. This prevents me of having to write lots of different ui:includes on each page.
For some popups it's necessary to prepare some data, which is done via the onclick attribute of an a4j:commandLink. Then, the oncomplete will show the actual popup like so:
<a4j:commandLink render="clientGroupMemberInfoPopup" rendered="#{assignmentDO.clientGroupMember}"
onclick="#{clientInfoBean.registerGmClientGroupMember(assignmentDO.gmClientGroupMemberDO)}"
oncomplete="RichFaces.ui.PopupPanel.showPopupPanel('ClientInfo')">
<h:graphicImage value="/img/icons/icon_info_sm.png" rendered="#{!printFriendly}"/>
</a4j:commandLink>
The corresponding bean:
#ManagedBean
#ViewScoped
public class ClientInfoBean {
#EJB
private ClientService clientService;
#Getter
#Setter
private ClientContextDO clientContextDO;
#Getter
#Setter
private GmClientGroupMemberDO gmClientGroupMemberDO;
#Getter
#Setter
private Long clientId;
public void registerGmClientGroupMember(final GmClientGroupMemberDO aGroupMember) {
gmClientGroupMemberDO = aGroupMember;
clientContextDO = clientService.findByClientId(gmClientGroupMemberDO.getClientId());
}
}
In this case above the rendered condition of the a4j:commandLink evaluates to true. However... the onclick is evaluated every single time, on every page this xhtml is included, once the rendered condition evaluates to true. Even when the page is still loading and nobody has clicked on anything yet!
Why? And what's the best way to prevent this? There's some relatively heavy db-stuff being done to prepare all the info necessary for the popup. I only want this stuff done the moment someone actually clicks on the link for the popup, not during page rendering phases.
There IS a duplicate of this question, I'm sure but I cannot find it. I'll remove this answer when BalusC flags it as such.
The onclick is for executing javascript, not accessing a server-side method. So the EL in it is evaluated as a value expression, not a method expression. So the output is considered as javascript. Consequently it is just evaluated at render time and re-evaluated when clicked.
The solution is to change the onclick to action
<a4j:commandLink render="clientGroupMemberInfoPopup" rendered="#{assignmentDO.clientGroupMember}"
action="#{clientInfoBean.registerGmClientGroupMember(assignmentDO.gmClientGroupMemberDO)}"
oncomplete="RichFaces.ui.PopupPanel.showPopupPanel('ClientInfo')">
<h:graphicImage value="/img/icons/icon_info_sm.png" rendered="#{!printFriendly}"/>
</a4j:commandLink>

preRenderComponent calls #PostConstruct multiple times

I am working on a page with a session-scoped managed bean.
On the page I have a preRenderComponent:
<f:metadata>
<f:event listener="#{pageBean.init}" type="preRenderComponent"></f:event>
</f:metadata>
This page is a template client, that shares a template with some other pages. The template contains a side navigation bar with links to each of the template client pages.
And the page bean:
#Named
#Default
#SessionScoped
public class pageBean implements Serializable {
#PostConstruct
public void init(){
System.out.println("Page Bean init.");
//call methods that populate data on the page
}
}
And the issue occurs as follows:
If I removed the line on the page with preRenderComponent, the pageBean init() method will still be called when the page is accessed.
If I kept the line said above, the init() method will be called when accessed, but it will also be called whenever I clicked on the side navigation bar and accessed another page which uses the same template.
I have referred to this question:CDI bean constructor and #PostConstruct called multiple times
and made sure I was not mixing JSF with CDI, yet this issue still occurs. Though it seems I could resolve this problem by just simply removing the preRenderComponent line, I really wish to understand what is going on here and figure out a way to avoid it in the future.
Any information is much appreciated.

<p:pickList> not clearing after RemoveAll button

I am encountering a strange problem with <p:pickList>. When I open a dialog having this pickList and clear all items from target list using the RemoveAll button and submit using <p:commandButton> the old items in the target list still appear. I have given the attribute required=true for the pickList. The strange behaviour is when I submit it, a tooltip appears saying that the field is mandatory.
So if the items are clearing and required attribute is working fine, why are the old items reappearing? What is the solution for this?
<p:pickList id="selectedId" value="#{someDialog.selectedItem}"
var="item" itemValue="#{item}" required="true"
converter="pickListConverter" requiredMessage="#{msg.required_message}"
styleClass="#{component.valid ? '': 'ui-state-error'}">
</p:pickList>
EDIT: Backing Bean - I am posting only the relevant method.
SomeDialog.java
private DualListModel<Item> selectedItem = new DualListModel<Item>();
//Its getters and setters
public final void afterSave(final ParamObject pObject) {
pObject.getRelevantData().clear();
pObject.getRelevantData().addAll(selectedItem.getTarget());
}
ParamObject .java
private List<Item> relevantData = new ArrayList<Item>();
//Setters and Getters
As #Xtreme Biker mentioned its in your backing bean.
When first time you submit list, you set it in backing bean. Second time you open dialog, it is already there. Even if you press RemoveAll, it removes only in your browser, it doesn't work with backing bean. And your submit will not work, because you have required=true.
So you can choose one of this options:
remove required=true and after every submit you will need press RemoveAll and submit again empty list (its not an option).
after doing all required tasks in backing bean, clear someDialog.selectedItem value, update and close dialog.
every time you open dialog clear someDialog.selectedItem value in backing bean.
I hope it will help as I am new in here too.

further continuing of double press

In a previous question BalusC gave me good advice on how a button, in place of a commandButton is useful for non ajax navigation. In particular it updates the destination address in the http: position which is useful for the user to bookmark a page.
I tried to use this information to my advantage until I came upon a problem. In a button I tried to use outcome="#{backing.something}" to find out that it gives me a null result. This looks like a timing problem in that action="#{}" is evaluated only when the button is pressed whereas outcome apparently wants a fixed string which gets checked when the page is loaded.
So I went back to commandButton with ajax="false". This has a problem that my navigation address is the page I came from, not the one I am navigating to. This is the wrong bookmark for the user.
I appreciate all the help I have received in stackoverflow on my learning exercise.
Ilan
The <h/p:button outcome> is not intented to invoke a bean action method, but to contain the outcome string directly. Any EL in there is evaluated immediately as a value expression. So the method behind it would immediately be invoked when you just open the page containing the <h/p:button>.
There are in your particular case basically two ways to invoke a bean action method on navigation. If you need to invoke it before the navigation takes place and the action isn't intented to be re-invoked everytime when the enduser reopens/reloads the GET request, then make it a POST-Redirect-GET request. It's a matter of adding faces-redirect=true to the outcome value in query string syntax.
E.g.
<p:commandButton action="#{bean.submit}" ... />
with
public String submit() {
// ...
return "nextpage?faces-redirect=true";
}
This way the browser will be redirected to the target page after POST, hence the enduser will see the target URL being reflected in the address bar.
Or if you need to invoke the action everytime when the enduser reopens/reloads the GET request, do the job in the (post)constructor or preRenderView listener method of the request/view scoped backing bean instead.
E.g.
<p:button outcome="nextpage" ... />
with
#ManagedBean
#RequestScoped
public class NextpageBacking {
public NextpageBacking() {
// In constructor.
}
#PostConstruct
public void onPostConstruct() {
// Or in postconstructor (will be invoked after construction AND injection).
}
public void onPreRenderView() {
// Or before rendering the view (will be invoked after all view params are set).
}
// ...
}
The pre render view listener method needs to be definied as follows in the nextpage
<f:event type="preRenderView" listener="#{nextpageBacking.onPreRenderView}" />
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters

Reset JSF Backing Bean(View or Session Scope)

I want to reset by JSF backing bean when some method is invoked. Assume that there is a command button, someone press it and after succesfull transaction, my View or Session scope JSF bean should be reseted. Is there a way to do that?
Thank
I found the solution for View scope.
public static void removeViewScopedBean(String beanName)
{
FacesContext.getCurrentInstance().getViewRoot().getViewMap().remove(beanName);
}
A view scoped bean will be recreated when you return non-null or non-void from the action method, even if it would go back to the same view. So, just return a String from the action method, even if it's just an empty string:
public String submit() {
// ...
return "";
}
To make it complete, you could consider sending a redirect by appending the ?faces-redirect=true query string to the returned outcome.
public String submit() {
// ...
return "viewId?faces-redirect=true";
}
A session scoped bean is in first place the wrong scope for whatever you're currently trying to achieve. The bean in question should have been be a view scoped one. Ignoring that, you could just recreate the model in the action method, or very maybe invalidate the session altogether (which would only also destroy all other view and session scoped beans, not sure if that is what you're after though).
just clear all views:
FacesContext.getCurrentInstance().getViewRoot().getViewMap().clear();
and remember to implements Serializable in all views
You could also refresh the page from javascript, so the ViewScoped Bean will be reseted, for example in a primefaces commandButton:
<p:commandButton value="Button" action="#{bean.someAction()}" oncomplete="location.reload()"/>
I solve the problem with code like this:
((HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest()).getSession().removeAttribute("bean name");
By this way I enter to session scoped bean and reset it without the data that was there before

Resources