Manually process `unload` event before triggering `preDestroy` method in OmniFaces ViewScoped bean - jsf

As a follow up to my question #PreDestroy method not called when leaving page of bean annotated with OmniFaces "ViewScoped" I have one other question:
Is it possible to process the JavaScript unload event manually before the #PreDestroy annotated method in an OmniFaces ViewScoped bean and only trigger the later conditionally if the user confirmed the question? I want to keep using the unload event to confirm whether the user really wants to leave the current page like this
window.onbeforeunload = (on) ? function() { return true; } : void 0;
and only bubble the event to the bean annotated with the OmniFaces #ViewScoped annotation and trigger the #PreDestroy annotated method if the user confirmed the initial Do you really want to leave the page? question by clicking Yes.
Is that possible?

Related

Does JSF prevent calls to unrendered managed bean actions by tampered requests

A method in a managed bean is protected by JSF? See the code:
Managed Bean
#ManagedBean
public class My {
public void test() {
System.out.println("called");
}
}
XHTML
<h:form>
<h:commandButton rendered="true" action="#{my.test}" value="Teste" />
</h:form>
If the button is not rendered (rendered="false"), a HTTP POST request (as the button would do) can be done and call the test() method?
In other words, JSF prevents calls to managed beans methods by tampered requests?
In other words, JSF prevents calls to managed beans methods by tampered requests?
Yes.
JSF re-evaluates the component's rendered attribute during apply request values phase. If it's false, then in case of UICommand components the ActionEvent simply won't be queued, regardless of whether the (tampered) HTTP request parameter indicates that the button is being pressed.
JSF has similar safeguard against tampered requests on the disabled and readonly attributes, also those of UIInput components. And, in UISelectOne/UISelectMany components, JSF will validate if the submitted value is indeed part of the provided available options.
JSF does this all also with help of the view state. If JSF were stateless, there would be more risk that one or other may fail if those attributes suddenly become request scoped instead of view scoped.
See also:
commandButton/commandLink/ajax action/listener method not invoked or input value not updated - point 5
Validation Error: Value is not valid
How to disable/enable JSF input field in JavaScript?
What is the usefulness of statelessness in JSF?

How to instantiate a backing bean on page load

For a project we are migrating some java applications to WebSphere 8.5. In the process we are trying to get rid of some legacy frameworks. One of them is shale (apache attic). The only component from shale that is used is the view controller to instantiate a request scoped jsf managed beans for every page. Every bean has an init method that is called on page load. I'd like to use #PostConstruct on this method. The only problem I have that the bean gets instantiated when a method on the bean is called. Unfortunately the bean is not always called and the init method does populate data on a session scoped bean. There is a naming convention that links pages and beans so we could use a listener to instantiate the bean based on the request. Another solution might be changing the scope to viewscope (probably to much a hassle on websphere 8.5).
I was wondering if there is something I can do to make the PostConstruct work? And are there other options I'm missing?
edit:
I have a PhaseListener in place that performs the basic functionality. It matches the requested page to the corresponding bean (by naming convention). The following is used to instantiate the bean but it looks a bit ugly.
expressionFactory.createValueExpression(elContext, "#{" + managedBeanName + "}", Object.class)
Is there a more elegant way to do this?
Perhaps you could try using <f:event/> ?
In your view, you could add this to the page.
<f:event type="postAddToView" listener="#{backingBean.myInitMethod()"/>
https://stackoverflow.com/a/14004230/4706826
Gives you info on when the events get executed.
Put a #PostConstruct annotated method in the backing bean. This annotation tells the bean to execute the annotated method every time its constructor is being called.
Example:
#ManagedBean
#ViewScoped
public class MyManagedBean{
#PostConstruct
public void initView() throws Exception{
...initialize page values, execute database queries, etc.
}

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

JSF 1.2: How to keep request scoped managed bean alive across postbacks on same view?

Is it possible to keep a request scoped bean alive across postbacks on the same page?
The general problem is, as the bean gets trashed on end of request and recreated on every form submit, for example the booleans behind dynamically manipulated disabled, readonly and rendered get reset to their default values and cause the forms to not work as intented anymore.
I'll assume that the session scope is not an option, otherwise this question makes little sense.
You can do it using Tomahawk <t:saveState>. Add the following line somewhere to the page:
<t:saveState value="#{bean}" />
RichFaces <a4j:keepAlive> does also the same:
<a4j:keepAlive beanName="#{bean}" />
Or if there is room, upgrade to at least JSF 2.x and put the bean in view scope:
#ManagedBean
#ViewScoped
public class Bean implements Serializable {
// ...
}
Regardless of the way, the same bean will be there when you postback to the same view and keep returning null or void from action methods.
See also:
How to choose the right bean scope?
Difference between View and Request scope in managed beans
Not really, unless you store the Bean somewhere e.g. a Map in application scope, to retrieve it later.
Why not just make it Session scoped? This is what Session scope is there for, so multiple Requests during the same Session can hit the same state.

Resources