View-scoped bean recreated on POST when URL parameters not used - jsf

I have a view-scoped JSF-managed bean that's backing an xhtml view where I read one parameter from the URL using f:viewParam.
The view presents a form to the user. However, when the user submits the form by pressing the p:commandButton it seems that the view-scoped bean is recreated (I added a #PostConstruct annotation to verify this) and so doesn't remember the instance variable read from the f:viewParam (invId in the code below).
I originally navigate to the view with a GET that includes a URL parameter but the POST message that's send when the user presses the p:commandButton doesn't include the URL parameter. So I am thinking that when the JSF runtime doesn't see the URL parameter on the POST it considers this to be a different view and is recreating the JSF-managed bean. When I change the view scope to session-scoped the code works.
Here's the code:
view
<f:metadata>
<f:viewParam name="invId" value="#{registerBean.invId}"/>
</f:metadata>
<h:form id="registrationForm">
....
<p:commandButton value="register" action="#{registerBean.register}"
icon="ui-icon ui-icon-newwin" ajax="false"/>
</h:form>
backing bean
#ManagedBean
#ViewScoped
public class RegisterBean implements Serializable {
#ManagedProperty(value="#{invId}")
private String invId;
...
update
It turns out that this wasn't related to the URL parameters at all. Following BalusC advice below I removed the c:when tags my view was using (relying on rendered attributes instead for the same effect), and now the view-scoped bean is no longer recreated and the invId field is properly retained.

The problem is not visible in the code posted so far, but it's for JSF 2.0/2.1 a known issue that a view scoped bean will be recreated when one of its properties is been bound to an attribute of a taghandler like JSTL <c:if> or Facelets <ui:include> or a view build time attribute of JSF component, such as id and binding, while partial state saving is enabled (as by default).
The background explanation is that any EL expressions in those attributes are executed during building and restoring the view. As view scoped beans are stored in the view and thus only available after restoring the view, such an EL expression evaluation would cause a brand new and separate view scoped bean to be created. This is basically a chicken-egg issue. It's fixed in the upcoming JSF 2.2.
There are basically 3 solutions:
Change the view accordingly so that those EL expressions are only evaluated during view render time. E.g. replace <c:if>/<c:choose> by rendered.
Or bind those attributes to a request scoped bean (design notice: you can just inject a view scoped bean as a managed property of a request scoped bean).
Turn off partial state saving, if necessary only for the particular view.
See also:
JSTL in JSF2 Facelets... makes sense?
#ViewScoped fails in taghandlers

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?

JSF Changing language from selectMenu doesn't invoke PostConstruct

I have in the header.xhtml change language selectOneMenu. Which by choosing changes the language properly of any xhtml file. But there are few pages where the language strings are set in the Java class itself which is invoked by the Post-Construct.
Header.xhtml
<h:selectOneMenu value="#{client.language}" onchange="submit()" >
<f:selectItems value="#{client.languages()}" />
</h:selectOneMenu>
In the managed bean, I have ViewScoped and then the PostConstruct. My problem here is- after changing the language from the menu, some string set by the Java are not translated. That's because it is invoked by the PostConstruct which is not invoked when the language is changed. But when I goto that page by clicking the link, then the strings are translated. Its just that as soon as I change language the strings aren't translated.
I think the problem here is because the PostConstruct is not invoked when the language is changed. How do I invoke it?
Your question is basically about the behavior of #PostConstruct. This method will be called once after the bean has been created and after the injection of fields has happened e.g. fields decorated with #EJB and #Resource annotations.
Since you use #ViewScoped bean, then this will be created once per view. Refreshing the page will create a new view, that's why your instance of the #ViewScoped bean will be recreated and you will process the data in the desired language.
Possible solutions:
Mark the bean that handles the view as #RequestScoped.
Use proper i18n internationalization for your output messages. Do not rely on messages being constructed from your managed bean.
In my case, I would use the latter rather than the former. Also, after changing the language, the best option is to fire a new non-ajax request-response cycle to the server.
More info:
How to choose the right bean scope?
Localization in JSF, how to remember selected locale per session instead of per request/view
I have tried to do it like you did in the past with no luck either. Here is how I work around the problem (using JQuery):
<h:selectOneMenu value="#{client.language}" onchange="$(document).find('.submitBtn').click();" >
<f:selectItems value="#{client.languages()}" />
</h:selectOneMenu>
<h:commandButton style="visibility: hidden;" styleClass="jsfHidden submitBtn" action="#{yourpostconstructmethod}"/>

JSF component binding - some confusion

From web pages like this one,
http://www.jsftutorials.net/components/step5.html
I understand that the binding attribute in JSF tag/view component is to bind the view component to a Java instance of the UI component in the backing bean.
E.g., that's what is done in the following code:
<h:inputText value="#{ myBean.someProperty}" binding="#{ myBean.somePropertyInputText}"/>
But sometimes I see code like this:
<h:commandButton id="t1" binding="#{foo}" value="Hello, World!" onclick="alert('I am #{id:cid(foo)}'); return false;" />
where id:cid is a taglib function which is defined as follow:
public static String cid(UIComponent component) {
FacesContext context = FacesContext.getCurrentInstance();
return component.getClientId(context);
}
In the above code, binding="#{foo}" does not bind to "a Java instance of the UI component in the backing bean".
So what is the meaning of expressions such as binding="#{foo}" ?
It just binds the component to the current Facelet scope. This is particularly useful if you don't need it in the backing bean at all. This saves your backing bean code from useless properties which aren't been used in any of the other methods at all. Note that it also works that way in JSF 1.2. Not sure about JSF 1.0/1.1 though as it uses a different and JSF-proprietary EL API.
See also:
JSF component binding without bean property

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.

h:inputText value not set in bean

The <h:inputText> value is not been set in a request scoped bean. If the value is preserved from DB, it works fine.
view:
<h:inputText id="receipient" size="90" styleClass="text" readonly="#{!bean.enable}" value="#{bean.recipient}" />
bean:
public class Bean {
private String recipient;
Bean(){
recipient = //load from db
}
//set
//get
}
What is the problem and how can I solve this?
Ensure that readonly="#{!bean.enable}" evaluates the same as it was in initial view. If it evaluates true during apply request values phase of the form submit, then the component's value won't be processed (because it is marked as read only).
An easy test is to put the bean in session scope. If that fixes it, then you know the culprit. If you're using JSF 2.0, just put bean in view scope by using #ViewScoped instead of #RequestScoped. If you're still on JSF 1.x, use Tomahawk's <t:saveState> component to let the bean act like the JSF 2.0 view scoped one.
<t:saveState value="#{bean}" />

Resources