When to use f:viewAction / preRenderView versus PostConstruct? - jsf

When should one use the f:viewAction or preRenderView event to initialize data for a page versus using the #PostConstruct annotation? Is the rationale to use one or the other based on the type of scope of the backing bean e.g. If the backing bean is #RequestScoped, then would the choice of using f:viewAction or preRenderView over #PostConstruct to initialize your backing bean prior to rendering the view be irrelevant as the two would result in the same effect?
f:viewAction or preRenderView
<f:metadata>
<f:viewAction action="#{myBean.initialize}" />
</f:metadata>
<f:metadata>
<f:event type="preRenderView" listener="#{myBean.initialize}"/>
</f:metadata>
or
#PostConstruct
public class MyBean
{
#PostConstruct
public void initialize()
{
}
}

When should one use the f:viewAction or preRenderView event to initialize data for a page verses using the #PostConstruct annotation?
Use the <f:viewAction> when you want to execute a method before the HTML is been rendered. This is particularly useful if you want to perform actions based on model values set by <f:viewParam> during update model values phase. Namely, they are not available at the moment the #PostConstruct runs. In JSF 2.0/2.1, this tag didn't exist and you have to use the preRenderView workaround.
If the backing bean is #RequestScoped, do they effectively do the exact same thing? (and so then it is up to developer choice? (#PostConstruct seems "cleaner").
No, they do definitely not effectively do the same thing. The #PostConstruct is intented to perform actions directly after bean's construction and setting of all injected dependencies and managed properties such as #EJB, #Inject, #ManagedProperty, etc. Namely, the injected dependencies are not available inside the bean's constructor. This will thus run only once per view, session or application when the bean is view, session or application scoped. The <f:viewAction> is by default only invoked on initial GET request, but can via onPostback="true" attribute be configured to be invoked on postback requests as well. The preRenderView event is invoked on every HTTP request (yes, this also includes ajax requests!).
Summarized, use #PostConstruct if you want to perform actions on injected dependencies and managed properties which are set by #EJB, #Inject, #ManagedProperty, etc during bean's construction. Use <f:viewAction> if you also want to perform actions on properties set by <f:viewParam>. If you're still on JSF 2.0/2.1, use preRenderView instead of <f:viewAction>. You can if necessary add a check on FacesContext#isPostback() to perform the preRenderView action on initial request only.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
ViewParam vs #ManagedProperty(value = "#{param.id}")
Is it possible to disable f:event type="preRenderView" listener on postback?

Do you need to initialize managed bean's properties? --> Then, use #PostConstruct
Otherwise, do you need to work with params passed from other view? --> Then, use "preRenderView"

Related

Missing FacesContext inside #WebServlet #doGet(...) [duplicate]

I've read how to send parameters using JSF but what if the user types their companyId in the URL when accessing their login page? For example,
http://my.company.url/productName/login.faces?companyId=acme.
The way we do it now, there is a bit of scriptlet code that grabs the value from the request and then set it in the session. That parameter changes their look and feel starting from the login page forward so each customer could have a different login page view. We are using extjs until I switch over to JSF.
Is there a way to do that using JSF 2 or perhaps PrimeFaces?
Yes, you can use the <f:viewParam> to set a request parameter as a managed bean property.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
</f:metadata>
You can if necessary invoke a bean action using <f:viewAction> (JSF 2.2+ only) or <f:event type="preRenderView">.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
When using <f:viewAction> you can even return a navigation outcome.
public String onload() {
// ...
return "somepage";
}
When not on JSF 2.2 yet, you can use ExternalContext#redirect() for that. See also among others How to perform navigation in preRenderView listener method.
Note that this is not specific to PrimeFaces. It's just part of standard JSF. PrimeFaces is merely a component library which provides enhanced ajax and skinnability support.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters
#ManagedProperty with request parameter not set in a #Named bean
url paramters can also be treated as request parameters so you can also access through
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
There is a utility library, OmniFaces which does this out of the box.
#Inject #Param
private String key;
#Inject #Param
private Long id;
You can use the request.getQueryString() if you want to get full query parameter string.

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: Passing bean to another JSF Page [duplicate]

I've read how to send parameters using JSF but what if the user types their companyId in the URL when accessing their login page? For example,
http://my.company.url/productName/login.faces?companyId=acme.
The way we do it now, there is a bit of scriptlet code that grabs the value from the request and then set it in the session. That parameter changes their look and feel starting from the login page forward so each customer could have a different login page view. We are using extjs until I switch over to JSF.
Is there a way to do that using JSF 2 or perhaps PrimeFaces?
Yes, you can use the <f:viewParam> to set a request parameter as a managed bean property.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
</f:metadata>
You can if necessary invoke a bean action using <f:viewAction> (JSF 2.2+ only) or <f:event type="preRenderView">.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
When using <f:viewAction> you can even return a navigation outcome.
public String onload() {
// ...
return "somepage";
}
When not on JSF 2.2 yet, you can use ExternalContext#redirect() for that. See also among others How to perform navigation in preRenderView listener method.
Note that this is not specific to PrimeFaces. It's just part of standard JSF. PrimeFaces is merely a component library which provides enhanced ajax and skinnability support.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters
#ManagedProperty with request parameter not set in a #Named bean
url paramters can also be treated as request parameters so you can also access through
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
There is a utility library, OmniFaces which does this out of the box.
#Inject #Param
private String key;
#Inject #Param
private Long id;
You can use the request.getQueryString() if you want to get full query parameter string.

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

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

How do I process GET query string URL parameters in backing bean on page load?

I've read how to send parameters using JSF but what if the user types their companyId in the URL when accessing their login page? For example,
http://my.company.url/productName/login.faces?companyId=acme.
The way we do it now, there is a bit of scriptlet code that grabs the value from the request and then set it in the session. That parameter changes their look and feel starting from the login page forward so each customer could have a different login page view. We are using extjs until I switch over to JSF.
Is there a way to do that using JSF 2 or perhaps PrimeFaces?
Yes, you can use the <f:viewParam> to set a request parameter as a managed bean property.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
</f:metadata>
You can if necessary invoke a bean action using <f:viewAction> (JSF 2.2+ only) or <f:event type="preRenderView">.
<f:metadata>
<f:viewParam name="companyId" value="#{bean.companyId}" />
<f:viewAction action="#{bean.onload}" />
</f:metadata>
When using <f:viewAction> you can even return a navigation outcome.
public String onload() {
// ...
return "somepage";
}
When not on JSF 2.2 yet, you can use ExternalContext#redirect() for that. See also among others How to perform navigation in preRenderView listener method.
Note that this is not specific to PrimeFaces. It's just part of standard JSF. PrimeFaces is merely a component library which provides enhanced ajax and skinnability support.
See also:
What can <f:metadata>, <f:viewParam> and <f:viewAction> be used for?
Communication in JSF 2.0 - Processing GET request parameters
#ManagedProperty with request parameter not set in a #Named bean
url paramters can also be treated as request parameters so you can also access through
FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap()
There is a utility library, OmniFaces which does this out of the box.
#Inject #Param
private String key;
#Inject #Param
private Long id;
You can use the request.getQueryString() if you want to get full query parameter string.

Resources