further continuing of double press - jsf

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

Related

Why does returning empty string from action method not recreate view?

I have a JSF page with a form that contains multiple textfields (p:inputtext) and a submit button. The page is backed by a ViewScoped backing bean. When the submit button is hit, an action method is called that returns an empty String ("").
According to this answer of BalusC, returning an empty string will refresh the view and recreate the ViewScoped backing bean.
However, when I submit my filled out form, the reloaded page still retains all my text input. How is this possible? Shouldn't the form be empty since the backing bean and view have been recreated?
#dmatob is right. When you have a JSF page backed by a ViewScoped bean:
If the method returns null, the bean won't be recreated (the values stay the same) but the page is reloaded.
If the method returns the same or another page, the bean will be recreated (it resets the values) and the page is reloaded.
I was facing the same few hours ago: trying to reset the values when the method is successfully executed. So after reading around and around, I found something that finally worked out:
You have to use action instead of actionListener (Differences here)
<p:commandButton value="Save" action="#{backingBean.save()}" />
So the method must return a String
public String save(){
if(validations==true){
return "currentpage.xhtml?faces-redirect=true";
}
return null;
}
When everything is okay, it will recreate the bean, refresh the page and reset the values. Otherwise the method returns null so it will refresh the page but the bean.
[EDITED]
If the method is returning null or empty String, the bean isn't recreated: the PostConstruct (init event) isn't being triggered, so that means the values stay the same. On the other case, if it returns a String (redirecting to some page), the init event is called so the values are initialized.
The JSF page is reloaded in both cases: when returning null/empty String or not.
Hope it helps you... Let me know ;-)
In a view scoped bean, only when your action method returns null, the bean doesn't initialize again.
If you want the action method to go back to the submitted form and reload the bean, your method must return the name of the page that contains the form.

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>

Why jsf life cycle skips render response after Invoke application when redirect the page?

I have two scenario
I have two actions which have logic in bean like
My buttons
<h:commandButton value="Submit" action="#{bean.test}" />
<h:commandButton value="Submit" action="#{bean.testOne}" />
This are the two commnadButtons and there logic in bean
public void test() {
log.info("I m in test");
}
public void testOne() {
log.info("I m in testOne");
try {
FacesContext.getCurrentInstance()
.addMessage("", new FacesMessage("Record Saved successfully."));
FacesContext
.getCurrentInstance()
.getExternalContext()
.redirect(
FacesContext.getCurrentInstance()
.getExternalContext().getRequestContextPath()
+ "/cycle/page");
} catch (IOException e) {
log.error(e.getMessage());
}
}
When I am going to click on 1st button (test action) that time jsf life cycle goes to every phase like restore view Apply request valuesProcess validationupdate modelsinvoke applicationrender response
But when I click on testOne button that time jsf life cycle skip render response after invoke application like restore view Apply request valuesProcess validationupdate modelsinvoke applicationrestore viewrender response
In simple language When I am navigate the page through facesContext that time jsf skips that phase.
But why this happens? I am not getting the problem.
According to the JSF docs, that's what the INVOKE_APPLICATION phase does:
During this phase, the JavaServer Faces implementation handles any application-level events, such as submitting a form or linking to another page.
At this point, if the application needs to redirect to a different web application resource or generate a response that does not contain any JavaServer Faces components, it can call the FacesContext.responseComplete method.
If the view being processed was reconstructed from state information from a previous request and if a component has fired an event, these events are broadcast to interested listeners.
Finally, the JavaServer Faces implementation transfers control to the Render Response phase.
What you're doing is calling FacesContext#getCurrentInstance#getExternalContext#redirect in this phase, which performs your redirection. This method triggers FacesContext#getCurrentInstance#responseComplete, which ends the JSF lifecycle. The redirection is performed to a JSF view, so a new cycle starts after that.
Apart from that and unrelated to the concrete issue, do not perform redirections like that if you want to redirect to an internal JSF view. Just return the navigation case + ?faces-redirect=true in your action method (i.e. /cycle/page?faces-redirect=true). Use the external context only if you want to access external urls.
See also:
JSF INVOKE_APPLICATION
Redirection in JSF

Jsf ui:repeat - method that populates the value is accessed even when submiting different form

In my actual project I have noticed that the method that populates the ui:repeat tag, is being invoked when there is a post call, even though the ui:repeat is not part of the submitted form.
I have been trying to check againts the jsf documentation if that is the way it should work, with no success.
Is it supposed to work this way?
Thanks in advance.
Sample code:
When the button is clicked the method anotherBean.getCollection is being invoked:
<h:form id="firstForm">
<h:commandButton action="#{someBean.someAction}"/>
</h:form>
<h:form id="secondForm">
<ui:repeat var="product" value="#{anotherBean.populatCollection}" >
<!-- CODE -->
</ui:repeat>
</h:form>
In first place, a getter method shouldn't be populating the value at all. A getter method should, as its name says, just return the already-populated value.
You're not terribly clear on the concrete functional requirement, i.e. when exactly did you intend to populate the value, but one way would be moving the populating logic to the #PostConstruct of #{anotherBean}.
#ManagedBean
#ViewScoped
public class AnotherBean {
private List<Something> getCollection; // Terrible variable name by the way.
#PostConstruct
public void init() {
getCollection = populateItSomehow();
}
public List<Something> getGetCollection() {
return getCollection; // See, just return the property, nothing more!
}
}
See also:
Why JSF calls getters multiple times
So, it looks like ui:repeat tag is invoking the methods assigned to its value argument when a post is done, no matter if the post is done from another form.
Thanks for the help.

CommandButton open new tab with FlashScope parameters

How can I open new tab when user clicks p:commandButton? I also want to pass some parameters to new page using FlashScope. Here's the code:
<h:form>
<p:commandButton value="open new tab" action="#{myBean.newTab}"/>
</h:form>
public String newTab() {
Faces.setFlashAttribute("foo", bar);
return "otherView";
}
On the otherView page I use f:event type="preRenderView" to read Flash parameters.
Two notes:
I need to use FlashScope, not URL parameters.
If possible, I don't want to change newTab() and preRenderView() methods.
Thanks for help
Use target="_blank" on the form to tell the browser that the synchronous response of the form should be presented in a new (blank) tab/window. You only need to turn off ajax behaviour of the <p:commandButton> in order to make it a synchronous request.
<h:form target="_blank">
<p:commandButton value="open new tab" action="#{myBean.newTab}" ajax="false" />
</h:form>
No changes are necessary in the backing beans, it'll just work as you intented. I would only recommend to use POST-Redirect-GET pattern in the action method.
return "otherView?faces-redirect=true";
Otherwise the new tab would show the URL of the originating page and a F5 would re-invoke the POST. Also, this way the flash scope is also really used as it is been designed for (if you didn't redirect, just storing in request scope was been sufficient).
Update: as per the comments, the view scoped bean in the initial tab/window get killed this way. by returning a String navigation case outcome. That's right, if you'd like to keep the view scoped bean alive, replace the navigation case by a Faces#redirect() call (assuming that it's indeed OmniFaces which you're using there for Faces#setFlashAttribute()). You only need to set Flash#setRedirect() to true beforehand to instruct the flash scope that a redirect will occur.
public void newTab() throws IOException {
Faces.setFlashAttribute("foo", bar);
Faces.getFlash().setRedirect(true);
Faces.redirect("otherView.xhtml");
}

Resources