Introduction
We assume that you've configured JSF to interpret empty string submitted values as null by the following context parameter inweb.xml
:<context-param> <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name> <param-value>true</param-value> </context-param>The above will prevent that model values get littered with empty strings when the enduser leaves input fields empty. If you're running Tomcat or a clone/fork of it (e.g. JBoss AS, WebSphere AS, etc) or at least a servletcontainer which utilizes Apache EL parser, then we also assume that you've the following startup VM argument set:
-Dorg.apache.el.parser.COERCE_TO_ZERO=falseThis will prevent that the aforementioned context parameter will fail for primitive wrapper managed bean properties (e.g.
Long
, Integer
, Double
, Boolean
, etc), because this EL parser would immediately coerce an empty string to the wrapped primitive's default value such as 0
, 0.0
or false
before returning the value to JSF. Glassfish for example does not have this problem.The code examples throughout the article are wherever applicable created and tested on Mojarra 2.1.3 and Glassfish 3.1.1 which are at the time of writing just the newest available. They should work equally good on older or newer versions or different implementations of JSF 2.x (MyFaces, for example) and/or servlet containers (Tomcat, JBoss, etc), unless explicitly otherwise mentioned. Any error/exception message examples are Mojarra 2.1.3 specific and may (slightly) differ on different versions and implementations.
The difference between JSF 2.0 and JSF 2.1 is subtle. As to compatibility, it's important to know that JSF 2.1 is targeted on Servlet 3.0 containers (Glassfish 3, Tomcat 7, JBoss AS 6, etc), while JSF 2.0 is targeted on older Servlet 2.5 containers (Glassfish 2, Tomcat 6, JBoss AS 5, etc). Most notable is Mojarra version 2.1.0; this version does not work on Tomcat/Jetty due to a major bug in the annotation scanner. This has been fixed in Mojarra 2.1.1.
Back to top
Managed bean names
You probably already know that you can annotate backing beans as managed bean with@ManagedBean
and specify the managed bean name by the name
attribute of the annotation without the need to specify them as <managed-bean>
boilerplate in faces-config.xml
like as in legacy JSF 1.x.package com.example.controller; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean(name="bean") @RequestScoped public class Bean { // ... }This way the managed bean is available in EL by
#{bean}
.
But do you also know that JSF will already implicitly use the bean's
name with 1st character lowercased (at least, conform the property
naming as stated in the Javabeans specification) when you omit the name
attribute altogether?package com.example.controller; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean @RequestScoped public class Bean { // ... }Make use of it and don't repeat yourself!
Back to top
Managed bean naming conventions
As to naming conventions, there is no strict convention specified by JSF itself. I've seen the following conventions:- Foo
- FooBean
- FooBacking
- FooManager
- FooController
- FooBackingBean
- FooManagedBean
- FooBB
- FooMB
FooBean
is too vague. Really a lot of classes can be
marked as javabeans. JSF managed beans, JPA entities, EJBs, service
classes, data transfer objects, value objects, etc. The Bean
suffix does not indicate the real responsibility of the class in any way. True, I use often MyBean
or Bean
in my generic code examples in blogs or forum/Q&A answers, but in real world you should avoid that.FooManagedBean
is a poor name, it's not only too long
and ugly, but technically, a managed bean is an instance of a backing
bean which is managed by some framework (JSF in this case). In JSF, the
class definition itself is really a backing bean, not a managed bean. So
a FooBackingBean
is technically more correct, but it's still too long and the Bean
part is still itchy.FooBB
and FooMB
are not clear enough and those abbreviations "BB" and "MB" are unlike e.g. "EJB" and "JPA" nowhere mentioned in the official Java EE documents. This convention should be avoided.Left behind the
Foo
, FooBacking
, FooManager
and FooController
.
In large projects, I personally tend to use "Manager" for session and
application scoped beans which are not tied to any input forms and
"Backing" for request and view scoped beans which are tied to input
forms. Or if you have a relatively small project, then you could use
"Controller" for all beans or even leave it entirely out.In any way, this is a pretty subjective question which can hardly be answered objectively with The One And Correct answer. It really doesn't matter that much to me or anyone else what you makes of it, as long as you're consistent with it throughout the entire project. Consistency is next to self-document-ability a very important aspect in a project.
Back to top
Managed bean scopes
JSF2 offers six predefinied@ManagedBean
scopes. Their lifetime and use are described in detail below:@RequestScoped
: a bean in this scope lives as long as the HTTP request-response lives. It get created upon a HTTP request and get destroyed when the HTTP response associated with the HTTP request is finished (this also applies to ajax requests!). JSF stores the bean as an attribute ofHttpServletRequest
with the managed bean name as key. It is also available byExternalContext#getRequestMap()
. Use this scope for pure request-scoped data. For example, plain vanilla GET requests which should present some dynamic data to the enduser depending on the parameters. You can also use this scope for simple non-ajax forms which do not require any model state during processing.@ViewScoped
: a bean in this scope lives as long as you're interacting with the same JSF view in the browser window/tab. It get created upon a HTTP request and get destroyed once you postback to a different view. It doesn't immediately get destroyed when you leave/close the view by a GET request, but it is not accessible the usual way anymore. JSF stores the bean in theUIViewRoot#getViewMap()
with the managed bean name as key, which is in turn stored in the session. You need to return null or void from action (listener) methods to keep the bean alive. Use this scope for more complex forms which use ajax, data tables and/or several rendered/disabled attributes whose state needs to be retained in the subsequent requests within the same browser window/tab (view).@SessionScoped
: a bean in this scope lives as long as the HTTP session lives. It get created upon the first HTTP request involving this bean in the session and get destroyed when the HTTP session is invalidated (or when you manually remove the bean from the session map). JSF stores the bean as an attribute ofHttpSession
with the managed bean name as key. It is also available byExternalContext#getSessionMap()
. Use this scope for pure session-scoped data which can safely be shared among all browser windows/tabs (views) within the same session. For example, the logged-in user, the user preferences such as user-specific settings and the chosen language/locale, etc.@ApplicationScoped
: a bean in this scope lives as long as the web application lives. It get created upon the first HTTP request involving this bean in the application (or when the web application starts up and theeager=true
attribute is set in@ManagedBean
) and get destroyed when the web application shuts down (or when you manually remove the bean from the application map). JSF stores the bean as an attribute of theServletContext
with the managed bean name as key. It is also available byExternalContext#getApplicationMap()
. Use this scope for pure application-scoped data which can safely be shared among all sessions. For example, constants such as country codes, static dropdown values, web application specific settings, etc.@NoneScoped
: a bean in this scope lives as long as a single EL evaluation. It get created upon an EL evaluation and get destroyed immediately after the EL evaluation. JSF does not store the bean anywhere. So if you have for example three#{bean.someProperty}
expressions in your view, then the bean get effectively created three times. Use this scope for a data bean which is purely supposed to be injected in another bean of a definied scope. The injected bean will then live as long as the acceptor bean.@CustomScoped
: a bean in this scope lives as long as the bean's entry in the customMap
which is created for this scope lives. You need to create and prepare thisMap
yourself in a broader scope, for example the session scope. You need to control the removal of the bean from theMap
yourself. Use this scope if no one of the other scopes suits the requirement, for example a conservation scope which spans across multiple views.
If the bean contains a mix of for example request-scoped and session-scoped data, then you should really split the beans in two different scoped beans. They can interact with each other by
@ManagedProperty
. See also the next chapter.Back to top
Injecting managed beans in each other
You can use@ManagedProperty
to inject among others @ManagedBean
instances in each other. This is particularly useful if you have a
request or view scoped bean associated with a page/form and would like
to have instant access to a session or application scoped bean. For
example, to get the currently logged-in user which is hold in a session
scoped managed bean so that you can perform actions based on the
currently logged-in user.Here's the session scoped one:
package com.example.controller; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.SessionScoped; @ManagedBean @SessionScoped public class ActiveUser implements Serializable { // ... }Here's the view scoped one:
package com.example.controller; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Products implements Serializable { // Properties --------------------------------------------------------------------------------- @ManagedProperty("#{activeUser}") private ActiveUser activeUser; // Init --------------------------------------------------------------------------------------- @PostConstruct public void init() { // You can do here your initialization thing based on managed properties, if necessary. } // Getters/setters ---------------------------------------------------------------------------- public void setActiveUser(ActiveUser activeUser) { this.activeUser = activeUser; } // ... }Note that a getter is not required in this particular case.
Also noted should be that you can't access any injected dependencies in the constructor of the bean yet. It is not possible to set/inject instance variables if the instance does not exist yet. The instance exists only if the constructor is finished. The earliest point to access an injected dependency is a
@PostConstruct
annotated method. Such a method is for demonstration purposes also shown in the above example. The method name init()
is by the way fully free to your choice. If you don't need such a method, then you can just omit it.Back to top
Injecting request parameters in a view scoped bean
You can only inject a managed property of the same or a broader scope. So for example injecting request parameters as a managed property in a view scoped bean is (unfortunately) not going to work.Following will not work:
package com.example.controller; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Bean implements Serializable { // Properties --------------------------------------------------------------------------------- @ManagedProperty("#{param.foo}") private String foo; // Getters/setters ---------------------------------------------------------------------------- public void setFoo(String foo) { this.foo = foo; } // ... }This will fail with the following error message:
Unable to create managed bean bean. The following problems were found: - The scope of the object referenced by expression #{param.foo}, request, is shorter than the referring managed beans (bean) scope of viewTo set request parameters in a view scoped bean, you would normally use
<f:viewParam>
tag as outlined in the chapter Processing GET request parameters. But it is possible to get them from the request parameter map during bean's (post)construction yourself.// Properties --------------------------------------------------------------------------------- private String foo; // Init --------------------------------------------------------------------------------------- @PostConstruct public void init() { FacesContext facesContext = FacesContext.getCurrentInstance(); this.foo = facesContext.getExternalContext().getRequestParameterMap().get(foo); }
Back to top
@ViewScoped fails in tag handlers (update: fixed since Mojarra 2.1.18!)
When you bind an attribute of a tag handler by an EL value expression to a view scoped bean, then it will create a brand new view scoped instance upon every request, even though it's a postback to the same view. This is a chicken-egg issue as stated in JSF issue 1492 which is fixed in JSF 2.2 and for Mojarra 2.1 backported in version 2.1.18. Simply put, JSF needs to restore the partial view in order to get the view state (and all view scoped beans) back. However, tag handlers runs during view build/restore time when JSF is about to construct the component tree. So they will run first and not be aware about any beans available in the view scope. When restoring the view is finished, the original view scoped beans are found and will be put back in the view scope. However, all EL value expressions of the tag handlers have already obtained the evaluated value of a completely different view scoped bean instance beforehand!Following is an overview of all tag handlers:
<c:choose>
<c:forEach>
<c:if>
<c:set>
<f:actionListener>
<f:convertXxx>
like<f:convertNumber>
<f:facet>
<f:validateXxx>
like<f:validateLongRange>
<f:valueChangeListener>
<ui:decorate>
<ui:composition>
<ui:include>
- any custom tag file
<ui:include src="#{viewScopedBean.includeFileName}.xhtml" />Then you need to turn off the partial state saving by the following context parameter in
web.xml
to get it to work properly:<context-param> <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name> <param-value>false</param-value> </context-param>Globally disabling the partial state saving has however the disadvantage that the view state size will grow in size. So if you're using server side state saving method, then the server's memory usage will increase, or if you're using client side state saving method, then the network bandwidth usage will increase. Basically, you get the same view state information as it was in JSF 1.x. If possible, you can also just disable the partial state saving on a per-view basis by the following context parameter in
web.xml
:<context-param> <param-name>javax.faces.FULL_STATE_SAVING_VIEW_IDS</param-name> <param-value>/some.xhtml,/other.xhtml</param-value> </context-param>It accepts a comma separated string of all view IDs for which the partial state saving should be disabled. If you do not want to disable it in any way, then you should really replace all EL usage in tag handlers by normal JSF components. Here's an overview of all tag handlers and the alternative approaches:
<c:choose>
: userendered
attribute instead<c:forEach>
: use<ui:repeat>
component instead<c:if>
: userendered
attribute instead<c:set>
: use<ui:param>
,<f:viewParam>
,@ManagedProperty
or@PostConstruct
instead<f:actionListener>
: useactionListener
attribute instead<f:convertXxx>
like<f:convertNumber>
: useconverter
attribute or<f:converter>
instead<f:facet>
: sorry, no alternative, just do not use EL on any of its attributes<f:validateXxx>
like<f:validateLongRange>
: usevalidator
attribute or<f:validator>
instead<f:valueChangeListener>
: usevalueChangeListener
attribute instead<ui:decorate>
: sorry, no alternative, just do not use EL on any of its attributes<ui:composition>
: sorry, no alternative, just do not use EL on any of its attributes<ui:include>
: use a bunch of<ui:fragment rendered>
components with each a static<ui:include>
- any custom tag file: make it a fullworthy JSF
UIComponent
instead
- JSTL in JSF2 Facelets... makes sense?
- @PostConstruct method is called even if the ManagedBean has already been instantiated (e.g. on AJAX-calls)
- ViewScoped bean getting constructed on every request… part 99
- Why does f:validateDoubleRange only work for @SessionScoped?
- JSF ViewScope calls constructor on every ajax request?
- @ViewScoped Managed bean loads many times during postback
Back to top
Implicit navigation
Ones who have worked with JSF 1.x navigation cases probably know what a hell of maintenance pain they can cause. Although, after all, in properly designed JSF web application their use should have been very minimal. POST form submits should navigate to the same page as the form and any results should be conditionally rendered on the very same page. In other words, the action methods should return an empty string, or null, or void anyway. Page-to-page navigation should take place by pure GET links, not by command links. This all is much better for user experience and SEO.However, now JSF2 comes with two new components
<h:link>
and <h:button>
which represents a pure GET link and button which both support the outcome
attribute representing the navigation case outcome, the new implicit
navigation support is more than welcome. You just have to specify the
view ID (basically, just the filename and the folder path, if any) and
JSF will take care about prefixing and/or suffixing the proper context
path and FacesServlet
mapping on the generated link and button in the HTML.For example, the following view
<h:link value="Home" outcome="home" /> <h:link value="FAQ" outcome="faq" /> <h:link value="Contact" outcome="contact" />will generate the following HTML
<a href="/contextname/home.xhtml">Home</a> <a href="/contextname/faq.xhtml">FAQ</a> <a href="/contextname/contact.xhtml">Contact</a>
Back to top
Implicit EL objects
JSF2 EL comes with a bunch of implicit EL objects which you can use in the view next to the managed beans. It are the following:#{component}
: the currentUIComponent
#{facesContext}
: the currentFacesContext
#{view}
: the currentUIViewRoot
#{request}
: the currentHttpServletRequest
#{session}
: the currentHttpSession
#{application}
: theServletContext
#{flash}
: the currentFlash
(which also implements map)#{cc}
: the current composite component#{requestScope}
: the current request attribute map#{viewScope}
: the current view attribute map#{sessionScope}
: the current session attribute map#{applicationScope}
: the application attribute map#{initParam}
: the current context parameter map#{param}
: the current request parameter map#{paramValues}
: the current request parameter values map#{header}
: the current request header map#{headerValues}
: the current request header values map#{cookie}
: the current request cookie map#{resource}
: converts a JSF resource identifier to a concrete resource URL. See chapter 5.6.2.5 of JSF specification
#{component}
one is interesting. It's like this in Java (and JavaScript) code. Below is an usage example:<h:inputText value="#{bean.value}" styleClass="#{component.valid ? '' : 'error'}" />The
#{component}
of a <h:inputText>
component refers to an instance of UIInput
class which in turn has an isValid()
method. So when you submit the form and a validation error occurs on the particular component, then the #{component.valid}
will evaluate false
which will then set the error
class on the input. This allows you to easily style invalid inputs!.error { background: #fee; }The
#{resource}
is primarily intented to be used in CSS
stylesheets to locate background images as a JSF resource. Below is an
usage example, assuming that the image file is identified by the
resource library "layout"
and the resource name "images/foo.png"
:.someClass { background-image: url("#{resource['layout:images/foo.png']}"); }Note that the CSS stylesheet itself should be included by
<h:outputStylesheet>
, otherwise EL in the CSS stylesheet won't be resolved.Back to top
Implicit output text
Since JSF 2.0 / Facelets, it's possible to inline EL in template text without the need to wrap it in a<h:outputText>
. This makes the source code better readable. Even more, Facelets will implicitly escape any HTML as well, like as in a real <h:outputText>
.<p>Welcome, #{activeUser.name}!</p>Only whenever you'd like to disable escaping using
escape="false"
, or would like to assign id
, styleClass
, onclick
, etc programmatically, then you still need <h:outputText>
.Back to top
Normal (synchronous) POST form
This is how a Facelet file with a normal POST form look like without any JSF2 ajax fanciness. Such a form would work perfectly fine in JSF1. We'll use this form as a kickoff to add JSF2 ajax fanciness in the following few chapters.<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> <h:head> <title>Page Title</title> </h:head> <h:body> <h:form id="form"> <h:panelGrid columns="3"> <h:outputLabel for="input1" value="Input 1" /> <h:inputText id="input1" value="#{bean.input1}" required="true" /> <h:message id="input1message" for="input1" /> <h:outputLabel for="input2" value="Input 2" /> <h:inputText id="input2" value="#{bean.input2}" required="true" /> <h:message id="input2message" for="input2" /> <h:panelGroup /> <h:commandButton value="Submit" action="#{bean.submit}" /> <h:messages id="messages" globalOnly="true" layout="table" /> </h:panelGrid> </h:form> </h:body> </html>Some notes to the HTML/CSS purists:
- Yes, a tableless form with a shitload of CSS would semantically have been more correct, but that's beyond the scope of this tutorial, we teach JSF, not CSS.
- Yes, a HTML5 doctype is perfectly fine. Long story short, read this: Is it possible to use JSF/Facelets with HTML4/5?
package com.example.controller; import javax.faces.application.FacesMessage; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.faces.context.FacesContext; @ManagedBean @RequestScoped public class Bean { // Properties --------------------------------------------------------------------------------- private String input1; private String input2; // Actions ------------------------------------------------------------------------------------ public void submit() { String message = String.format("Submitted: input1=%s, input2=%s", input1, input2); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message)); } // Getters/setters ---------------------------------------------------------------------------- public String getInput1() { return input1; } public void setInput1(String input1) { this.input1 = input1; } public String getInput2() { return input2; } public void setInput2(String input2) { this.input2 = input2; } }
Back to top
Ajax (asynchronous) POST form
Turning a normal form into an ajax form is a matter of adding<f:ajax>
to the UICommand
component.<h:commandButton value="Submit" action="#{bean.submit}"> <f:ajax execute="@form" render="@form" /> </h:commandButton>This requires that you're using
<h:head>
instead of <head>
, otherwise the JSF-bundled Ajax helper JavaScript file won't be auto-included! When you run JSF with project stage set to "Development"
by the following context parameter in web.xml
,<context-param> <param-name>javax.faces.PROJECT_STAGE</param-name> <param-value>Development</param-value> </context-param>then you would be noticed about this mistake by the following development error message:
· One or more resources have the target of 'head', but no 'head' component has been defined within the view.Coming back to the JSF2
<f:ajax>
tag:The
<f:ajax>
's execute
attribute defaults to @this
(which is in this particular case the sole UICommand component itself).
So input values won't be submitted. You need to explicitly specify execute="@form"
to submit the whole form. Or when you want to submit specific input components only, such as the first one, then use execute="@this input1"
. Yes, it's space separated (PrimeFaces for example also supports comma separation, but JSF2 thus not). Using @this
is mandatory in order to let the button's action to execute anyway.The
<f:ajax>
's render
attribute defaults to @none
. You would like to render the entire form, so that validation messages will show up. You need to explicitly specify render="@form"
then. Alternatively, maybe to save bandwidth, you can also specify to render only the message components. In this case, use render="input1message input2message messages"
. This may however end up to be cumbersome and hard-to-maintain.The
<f:ajax>
has also an event
attribute. The default value depends on the enclosing component. In case of UICommand
components this defaults to event="action"
which is perfectly fine to submit a form. JSF will then take care that
the right HTML DOM event is been used to hook on an action, which is "click"
in case of a standard command button and link.Back to top
Ajax validation
This is a matter of adding<f:ajax>
to the UIInput
component with a render ID which points to the associated message component(s).<h:outputLabel for="input1" value="Input 1" /> <h:inputText id="input1" value="#{bean.input1}" required="true"> <f:ajax event="blur" render="input1message" /> </h:inputText> <h:message id="input1message" for="input1" /> <h:outputLabel for="input2" value="Input 2" /> <h:inputText id="input2" value="#{bean.input2}" required="true"> <f:ajax event="blur" render="input2message" /> </h:inputText> <h:message id="input2message" for="input2" /> <h:panelGroup /> <h:commandButton value="Submit" action="#{bean.submit}"> <f:ajax execute="@form" render="input1message input2message messages" /> </h:commandButton> <h:messages id="messages" globalOnly="true" layout="table" />The
<f:ajax>
's event
attribute defaults in UIInput
components to event="valueChange"
. JSF will then take care that the right HTML DOM event is been used to hook on a value change, which is "change"
in case of text inputs and dropdowns and which is "click"
in case of checkboxes and radiobuttons. However, in case of text
inputs, when you tab along the required fields without entering a value,
then the change event won't be fired. This may be affordable in some
cases, but for the case that you would like to validate the text input
immediately when the user leaves the field, then rather use event="blur"
.Note that the
<f:ajax>
's render
attribute of the UICommand
component in the above example has been changed to render the message components only. You can also use render="@form"
here. Both approaches are equally fine. It's a matter of taste and maintainability.The managed bean
#{bean}
can be placed in the request scope
for such a form. But, with a big BUT, a request scoped managed bean
will be recreated on every ajax request! You would like to use a view
scoped managed bean instead. It'll live as long as you're firing ajax
requests from the same view on and the regular action methods returns
all null or void.
Once a managed bean action method returns a valid navigation case
outcome, even though it's to the same view, the view scoped managed bean
will be garbaged and recreated.package com.example.controller; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; @ManagedBean @ViewScoped public class Bean implements Serializable { // ... }
Back to top
Ajax rendering of content outside form
Render IDs are resolved relative to the parentUINamingContainer
component. Examples of UINamingContainer
components are <h:form>
, <h:dataTable>
, <ui:repeat>
, etc. It are those components which prepends the client ID of their children with its own client ID.Following will not work:
<h:form id="form"> <h:commandButton value="Submit"> <f:ajax render="result" /> </h:commandButton> </h:form> <h:outputText id="result" value="#{bean.result}" />Some JSF implementations/libraries would immediately show an error for this, either as a HTTP 500 error or some warning in the server log. Mojarra for example would throw an exception with a message like this (the
j_idt6
is in this particular case just the autogenerated ID of the command button): <f:ajax> contains an unknown id 'result' - cannot locate it in the context of the component j_idt6If you prefix render ID with
":"
then it's resolved relative to the view root. It becomes the absolute render ID.Following will work:
<h:form id="form"> <h:commandButton value="Submit"> <f:ajax render=":result" /> </h:commandButton> </h:form> <h:outputText id="result" value="#{bean.result}" />The absolute render ID needs to be the full client ID prefixed with
":"
. So if the render target is by itself nested in another UINamingContainer
component, then you need to give it a fixed ID and include its ID as well.<h:form id="form"> <h:commandButton value="Submit"> <f:ajax render=":otherform:result" /> </h:commandButton> </h:form> <h:form id="otherform"> <h:outputText id="result" value="#{bean.result}" /> </h:form>An easy way to check for the right render ID is to check the client ID of the JSF-generated HTML element. Open the page in a webbrowser, rightclick and choose View Source. That's the JSF-generated HTML output. You need to locate the HTML element which has been generated by the JSF component in question. In case of a
<h:outputText id="result">
which is been enclosed in a <h:form id="otherform">
it'll look like the following:<span id="otherform:result"></span>You need to grab exactly that client ID and then prefix with
":"
for the absolute render ID.Back to top
Ajax rendering of content which contains another form
When you ajax render some content which in turn contains another form, then it won't work flawlessly because the other form would lose its view state. The view state is stored in a hidden input field with the namejavax.faces.ViewState
. The JSF JavaScript library is supposed to re-add the hidden field after ajax render.Following will not work:
<h:panelGroup id="firstPanel"> <h:form id="firstForm"> <h:outputLabel for="input" value="First form input" /> <h:inputText id="input" value="#{bean1.input}" required="true" /> <h:commandButton value="Submit form" action="#{bean1.submit}"> <f:ajax execute="@form" render="@form :secondPanel :messages" /> </h:commandButton> <h:message for="input" /> </h:form> </h:panelGroup> <h:panelGroup id="secondPanel"> <h:form id="secondForm"> <h:outputLabel for="input" value="Second form input" /> <h:inputText id="input" value="#{bean2.input}" required="true" /> <h:commandButton value="Submit other form" action="#{bean2.submit}"> <f:ajax execute="@form" render="@form :firstPanel :messages" /> </h:commandButton> <h:message for="input" /> </h:form> </h:panelGroup> <h:messages id="messages" globalOnly="true" layout="table" />Now, leave the fields empty and click the submit button of the first form. A validation error shows for the first input. This is fine. Now click the submit button of the second form with the fields still empty. You'd expect that a validation error shows for the second input. But it does not show up! Only the validation error of the first form disappears. When you would have entered the field, the managed bean action method of the second form would not be invoked. You need to click the button twice to get it to work. Repeat then the same on the first form and so on. You need to enter the field and click the button once again everytime to get the form to be submitted.
What is happening here is that the
javax.faces.ViewState
hidden input field is not added to the other form. This is an issue in
the JSF JavaScript library and already acknowledged by the JSF spec guys
and is currently been scheduled to be fixed in 2.3: JSF spec issue 790.
Both Mojarra and MyFaces exposed the same issue, however MyFaces has
fixed it rather quickly while Mojarra 2.2 still hasn't fixed. To get it
to work you would need to explicitly add the client ID of the other form
in the <f:ajax>
render.<h:panelGroup id="firstPanel"> <h:form id="firstForm"> <h:outputLabel for="input" value="First form input" /> <h:inputText id="input" value="#{bean1.input}" required="true" /> <h:commandButton value="Submit form" action="#{bean1.submit}"> <f:ajax execute="@form" render="@form :secondPanel :secondForm :messages" /> </h:commandButton> <h:message for="input" /> </h:form> </h:panelGroup> <h:panelGroup id="secondPanel"> <h:form id="secondForm"> <h:outputLabel for="input" value="Second form input" /> <h:inputText id="input" value="#{bean2.input}" required="true" /> <h:commandButton value="Submit other form" action="#{bean2.submit}"> <f:ajax execute="@form" render="@form :firstPanel :firstForm :messages" /> </h:commandButton> <h:message for="input" /> </h:form> </h:panelGroup> <h:messages id="messages" globalOnly="true" layout="table" />No, this does not cause an overhead in the JSF ajax response. Since the component referenced by
:secondForm
is already a child of the component referenced by :secondPanel
, this will just be skipped in the JSF ajax response. But the JSF JavaScript library will now add the mandatory javax.faces.ViewState
hidden input field to the second form.Back to top
Automatically fix missing JSF view state after ajax rendering
An alternative to explicitly specifying the forms in ajax render is to include this piece of JavaScript which should automatically detect any missingjavax.faces.ViewState
hidden input fields and automatically append them:jsf.ajax.addOnEvent(function(data) { if (data.status == "success") { var viewState = getViewState(data.responseXML); for (var i = 0; i < document.forms.length; i++) { var form = document.forms[i]; if (form.method == "post" && !hasViewState(form)) { createViewState(form, viewState); } } } }); function getViewState(responseXML) { var updates = responseXML.getElementsByTagName("update"); for (var i = 0; i < updates.length; i++) { if (updates[i].getAttribute("id").match(/^([\w]+:)?javax\.faces\.ViewState(:[0-9]+)?$/)) { return updates[i].firstChild.nodeValue; } } return null; } function hasViewState(form) { for (var i = 0; i < form.elements.length; i++) { if (form.elements[i].name == "javax.faces.ViewState") { return true; } } return false; } function createViewState(form, viewState) { var hidden; try { hidden = document.createElement("<input name='javax.faces.ViewState'>"); // IE6-8. } catch(e) { hidden = document.createElement("input"); hidden.setAttribute("name", "javax.faces.ViewState"); } hidden.setAttribute("type", "hidden"); hidden.setAttribute("value", viewState); hidden.setAttribute("autocomplete", "off"); form.appendChild(hidden); }Note that when you're using PrimeFaces and its own ajax components like
<p:commandButton>
and so on, then you don't need the above workaround; they have already fixed this matter in their own core ajax engine.Back to top
Ajax rendering of content which is by itself conditionally rendered
Ajax rendering takes place fully at the client side and expects the to-be-updated HTML element to be already in the JSF-generated HTML output.Following will not work when
#{bean.renderResult}
defaults to false
:<h:form id="form"> <h:commandButton value="Submit" action="#{bean.toggleRenderResult}"> <f:ajax render=":result" /> </h:commandButton> </h:form> <h:outputText id="result" value="#{bean.result}" rendered="#{bean.renderResult}" />You need to ensure that the render ID points to a component which is already present in the JSF-generated HTML output.
Following will work:
<h:form id="form"> <h:commandButton value="Submit" action="#{bean.toggleRenderResult}"> <f:ajax render=":result" /> </h:commandButton> </h:form> <h:panelGroup id="result"> <h:outputText value="#{bean.result}" rendered="#{bean.renderResult}" /> </h:panelGroup>
Back to top
Passing POST action method arguments
Since EL 2.2, which is maintained as part of Servlet 3.0 / JSP 2.2 (thus, Tomcat 7, Glassfish 3, JBoss AS 6, etc or newer), it is possible to pass fullworthy objects as JSF action method arguments.<h:form> <h:dataTable value="#{bean.users}" var="user"> <h:column>#{user.id}</h:column> <h:column>#{user.name}</h:column> <h:column>#{user.email}</h:column> <h:column><h:commandButton value="Edit" action="#{bean.edit(user)}" /></h:column> </h:dataTable> </h:form>In combination with this bean method:
public void edit(User user) { this.user = user; this.edit = true; }See also the article Benefits and pitfalls of @ViewScoped - Really simple CRUD, now without DataModel
Another interesting use case is the following:
<h:form> <f:ajax render=":include"> <h:commandLink value="Home" action="#{menu.setPage('home')}" /><br /> <h:commandLink value="FAQ" action="#{menu.setPage('faq')}" /><br /> <h:commandLink value="Contact" action="#{menu.setPage('contact')}" /><br /> </f:ajax> </h:form> <h:panelGroup id="include"> <ui:include src="#{menu.page}.xhtml" /> </h:panelGroup>Note that you should specify the full setter method name in
action
attribute! Also note that this approach fails when the include page contains a form.When you're still targeting an old Servlet 2.5 / JSP 2.1 container (e.g., Tomcat 6, Glassfish 2, JBoss AS 5, etc), then you can always install JBoss EL to add the same enhancements for EL 2.1. Just grab and drop jboss-el-2.0.0.GA.jar in your webapp's
/WEB-INF/lib
folder and add the following (Mojarra-specific!) context parameter to your web.xml
:<context-param> <param-name>com.sun.faces.expressionFactory</param-name> <param-value>org.jboss.el.ExpressionFactoryImpl</param-value> </context-param>
Back to top
Processing GET request parameters
JSF2 offers the new<f:viewParam>
tag to set GET request parameters in a managed bean. Back in old JSF 1.x you could also set them as a managed property in faces-config.xml
, however that works on request scoped beans only and it does not allow for fine-grained conversion and validation. The new @ManagedProperty
annotation does nothing different. The <f:viewParam>
works on view scoped beans as well (and also on session and application
scoped ones, but the benefit is questionable). It allows for
declarative conversion and validation with <f:converter>
and <f:validator>
and even a <h:message>
can be attached to it.Let's first start with creating GET links with request parameters in a list:
<ul> <ui:repeat value="#{bean.users}" var="user"> <li> <h:link value="View details of #{user.name}" outcome="user"> <f:param name="id" value="#{user.id}" /> </h:link> </li> </ui:repeat> </ul>The
<h:link>
will end up something like the following in the HTML:<ul> <li><a href="/contextname/user.xhtml?id=123">View details of BalusC</a></li> <li><a href="/contextname/user.xhtml?id=456">View details of John Doe</a></li> <li><a href="/contextname/user.xhtml?id=789">View details of Nobody</a></li> </ul>In the
user.xhtml
file, you can at its simplest use <f:viewParam>
to set the id
as a property of the managed bean and use <f:event type="preRenderView">
to trigger a managed bean method whenever all view parameters have been set:<f:metadata> <f:viewParam name="id" value="#{bean.userId}" /> <f:event type="preRenderView" listener="#{bean.init}" /> </f:metadata> <h:head> <title>User Details</title> </h:head> <h:body> <h:messages /> <h:panelGrid columns="2" rendered="#{not empty bean.user}"> <h:outputText value="ID" /> <h:outputText value="#{bean.user.id}" /> <h:outputText value="Name" /> <h:outputText value="#{bean.user.name}" /> <h:outputText value="Email" /> <h:outputText value="#{bean.user.email}" /> </h:panelGrid> <h:link value="Back to all users" outcome="users" /> </h:body>Note: as per the upcoming JSF 2.2, the
<f:event type="preRenderView">
should be replaced by a more self-documenting <f:viewAction>
, see also this article of my fellow Arjan Tijms: What's new in JSF 2.2?<f:viewAction action="#{bean.init}" />Here's how the backing bean can look like:
package com.example.controller; import java.io.Serializable; import javax.faces.bean.ManagedBean; import javax.faces.bean.ViewScoped; import com.example.business.UserService; import com.example.model.User; @ManagedBean @ViewScoped public class Bean implements Serializable { // Properties --------------------------------------------------------------------------------- private Long userId; private User user; // Services ----------------------------------------------------------------------------------- @EJB private UserService userService; // Actions ------------------------------------------------------------------------------------ public void init() { if (userId == null) { String message = "Bad request. Please use a link from within the system."; FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null)); return; } user = userService.find(userId); if (user == null) { String message = "Bad request. Unknown user."; FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, message, null)); } } // Getters/setters ---------------------------------------------------------------------------- public Long getUserId() { return userId; } public void setUserId(Long userId) { this.userId = userId; } public User getUser() { return user; } }Note that there's some unnecessary boilerplate code in the backing bean. Ultimately, you would like to end up with only the
User
property. Check the next chapter.Back to top
Converting and validating GET request parameters
You see, in the previous chapter there's some boilerplate in theinit()
which does the conversion (and essentially also validation). This can
be improved by extracting the conversion from the backing bean into a
fullworthy and reuseable Converter
class as follows:package com.example.converter; import javax.ejb.EJB; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import com.example.business.UserService; import com.example.model.User; @ManagedBean @RequestScoped // Can also be @ApplicationScoped if the Converter is entirely stateless. public class UserConverter implements Converter { // Services ----------------------------------------------------------------------------------- @EJB private UserService userService; // Actions ------------------------------------------------------------------------------------ @Override public String getAsString(FacesContext context, UIComponent component, Object modelValue) { if (modelValue == null) { return ""; } if (modelValue instanceof User) { return String.valueOf(((User) modelValue).getId()); } else { throw new ConverterException(new FacesMessage(modelValue + "is not a valid User entity")); } } @Override public Object getAsObject(FacesContext context, UIComponent component, String submittedValue) { if (submittedValue == null || submittedValue.isEmpty()) { return null; } try { return userService.find(Long.valueOf(submittedValue)); } catch (NumberFormatException e) { throw new ConverterException(new FacesMessage(submittedValue + " is not a valid User ID", e)); } } }Yes, it's a
@ManagedBean
instead of @FacesConverter
! How awkward, but it's not possible to inject an @EJB
inside a @FacesConverter
in order to do the DB interaction job. The same problem manifests when you use the CDI @Inject
to inject a property, you'd need to use @Named
instead of @FacesConverter
. The Java EE/JSF/CDI guys are working on that for the future JSF 2.2 version, see also JSF spec issue 763. Update:
unfortunately, it didn't made into JSF 2.2. It has been postponed to
JSF 2.3. JSF utility library OmniFaces will have a CDI compatible @FacesConverter/FacesValidator
in version 1.6.If you really need to have it to be a
@FacesConverter
(in order to utilize the forClass
attribute, for example), then you can always manually grab the EJB from JNDI. See also the next chapter.Now, you can specify the above converter in the
converter
attribute as follows and validate it as required
as well. The appropriate error messages can be specified by converterMessage
and requiredMessage
respectively. Please note that you need to specify the converter in the view using converter="#{userConverter}"
instead of converter="userConverter"
because it's @ManagedBean
instead of @FacesConverter
.<f:metadata> <f:viewParam name="id" value="#{bean.user}" converter="#{userConverter}" converterMessage="Bad request. Unknown user." required="true" requiredMessage="Bad request. Please use a link from within the system." /> </f:metadata>Now we can make the backing bean more terse:
// Properties --------------------------------------------------------------------------------- private User user; // Getters/setters ---------------------------------------------------------------------------- public User getUser() { return user; } public void setUser(User user) { this.user = user; }
Back to top
Getting an EJB in @FacesConverter and @FacesValidator
There's a way to get an EJB in a@FacesConverter
and @FacesValidator
.
You only have to manually lookup it from JNDI. First create some
utility class which does the job. The below one assumes that EJBs are
deployed in the WAR (as you would do in Java EE 6 Web Profile), for EARs
you'd need to alter the EJB_CONTEXT
to add the EJB module name:package com.example.util; import javax.naming.InitialContext; import javax.naming.NamingException; /** * Utility class for EJBs. There's a {@link #lookup(Class)} method which allows you to lookup the * current instance of a given EJB class from the JNDI context. This utility class assumes that * EJBs are deployed in the WAR as you would do in Java EE 6 Web Profile. For EARs, you'd need to * alter the <code>EJB_CONTEXT</code> to add the EJB module name or to add another lookup() method. */ public final class EJB { // Constants ---------------------------------------------------------------------------------- private static final String EJB_CONTEXT; static { try { EJB_CONTEXT = "java:global/" + new InitialContext().lookup("java:app/AppName") + "/"; } catch (NamingException e) { throw new ExceptionInInitializerError(e); } } // Constructors ------------------------------------------------------------------------------- private EJB() { // Utility class, so hide default constructor. } // Helpers ------------------------------------------------------------------------------------ /** * Lookup the current instance of the given EJB class from the JNDI context. If the given class * implements a local or remote interface, you must assign the return type to that interface to * prevent ClassCastException. No-interface EJB lookups can just be assigned to own type. E.g. * <li><code>IfaceEJB ifaceEJB = EJB.lookup(ConcreteEJB.class);</code> * <li><code>NoIfaceEJB noIfaceEJB = EJB.lookup(NoIfaceEJB.class);</code> * @param <T> The EJB type. * @param ejbClass The EJB class. * @return The instance of the given EJB class from the JNDI context. * @throws IllegalArgumentException If the given EJB class cannot be found in the JNDI context. */ @SuppressWarnings("unchecked") // Because of forced cast on (T). public static <T> T lookup(Class<T> ejbClass) { String jndiName = EJB_CONTEXT + ejbClass.getSimpleName(); try { // Do not use ejbClass.cast(). It will fail on local/remote interfaces. return (T) new InitialContext().lookup(jndiName); } catch (NamingException e) { throw new IllegalArgumentException( String.format("Cannot find EJB class %s in JNDI %s", ejbClass, jndiName), e); } } }Note that this works in a Java EE 6 container only! In Java EE 5 and before, there's no way to obtain the application name from JNDI. You would need to hardcode it yourself or provide it by some configuration file (e.g. properties or XML file) or as VM argument.
Now you can turn the
UserConverter
into a fullworthy @FacesConverter
:package com.example.converter; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.convert.Converter; import javax.faces.convert.ConverterException; import javax.faces.convert.FacesConverter; import com.example.business.UserService; import com.example.model.User; import com.example.util.EJB; @FacesConverter(forClass=User.class) public class UserConverter implements Converter { // Services ----------------------------------------------------------------------------------- private UserService userService = EJB.lookup(UserService.class); // ... }Thanks to the
forClass
attribute of the @FacesConverter
,
it will always be automatically invoked whenever a managed bean
property of exactly the given type is to be converted. So, you can omit
the converter
attribute from the <f:viewParam>
:<f:metadata> <f:viewParam name="id" value="#{bean.user}" converterMessage="Bad request. Unknown user." required="true" requiredMessage="Bad request. Please use a link from within the system." /> </f:metadata>
Read the article Node.js vs Ruby on Rails https://www.cleveroad.com/blog/node-js-vs-ruby-on-rails--the-power-behind-technologies
ReplyDelete