The
view scope lives as long as you are
navigating in the same JSF
view in the browser window/tab.
The view scope is useful when you need to preserve data over multiple
requests without leaving the current JSF view by clicking on a link, returning
a different action outcome, or any other interaction that dumps the current
view. It gets created upon an HTTP request and gets destroyed when you postback
to a different view; as long as you postback to the same view, the view scope
is alive. Since the view scope is particularly useful when you are editing some
objects while staying in the same view, it can be the perfect choice for rich
AJAX requests. Moreover, since the view scope is bounded to the current view,
it does not reflect the stored information in another window or tab of a
browser; this is an issue specific to
the session scope!
! In order to keep the view active, the bean methods
(actions/listeners) must return null or void.
View Scope Annotations
JSF: The JSF view scope
annotation is @ViewScoped (javax.faces.bean.ViewScoped). A bean with this scope
should be annotated with @ManagedBean
(javax.faces.bean.ManagedBean).
The default scope is @RequestScope.
CDI: The view scope is not
available in CDI, but JSF 2.2 has introduced it through the new annotation, @ViewScoped. This is
defined in the javax.faces.view.ViewScoped
package and it is compatible with CDI. Do not confuse this @ViewScoped with the one
defined in the javax.faces.bean
package, which is JSF compatible! So, the CDI view scope annotation is @ViewScoped (javax.faces.view.ViewScoped). A bean with this scope
should be annotated with @Named
(javax.inject.Named).
For CDI managed beans (@Named),
the default scope is the @Dependent
pseudo-scope.
Simple Example
// index.xhtml
<h:body>
<h4>Same view after submit
(index.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionVoid()}"/>
</h:form>
Current value: #{countBean.count}
<h4>Forward to another view after submit
(count.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionAndForward()}"/>
</h:form>
Current value: #{countBean.count}
<h4>Redirect to another view after
submit (count.xhtml):</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionAndRedirect()}"/>
</h:form>
Current value: #{countBean.count}
<h4>AJAX :</h4>
<h:form>
<h:commandButton value="Count"
action="#{countBean.countActionVoid()}">
<f:ajax render="currentValueId"/>
</h:commandButton>
</h:form>
<h:outputText id="currentValueId"
value="Current value: #{countBean.count}"/>
</h:body>
// count.xhtml
<h:body>
Current
value: #{countBean.count}
</h:body>
// CountBean.java
import
java.util.logging.Logger;
import java.io.Serializable;
// for JSF
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
// for CDI
import javax.inject.Named;
import javax.faces.view.ViewScoped;
// JSF vs CDI
@ManagedBean @Named
@ViewScoped @ViewScoped
public class
CountBean implements
Serializable {
private static final Logger LOG =
Logger.getLogger(CountBean.class.getName());
private int count;
public CountBean() {
LOG.info("CountBean#Initializing counter
...");
count = 0;
}
public void countActionVoid() {
LOG.info("CountBean#countActionVoid() -
Increasing counter ...");
count++;
}
public String countActionAndForward() {
LOG.info("CountBean#countActionAndForward() - Increasing counter
...");
count++;
return "count";
}
public String countActionAndRedirect() {
LOG.info("CountBean#countActionAndRedirect() - Increasing counter
...");
count++;
return
"count?faces-redirect=true;";
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
The complete application is available here.
So, when navigating via AJAX requests or via implicit forward mechanism back in the same view then the count value will be
increased by 1. Navigating
via redirect or explicit forward (including
explicit navigation to the current
view) will reset/reinitialize the count. This reveals
two aspects:
- The CountBean
constructor is called for creating a new instance once per view. This means that count is initialized with 0 only once. Further
requests fired in the current view will use this CountBean instance. We say that there is a CountBean instance per
view.
- The view scope doesn't lose the object's state while forwarding to
the same view (except the case when this is done explicitly from page or a managed bean method return). The object's
state is available until the view is destroyed (e.g. expires) or you navigate
to another view/window/tab or explicitly
to the same view/tab/window.
Basically you have to pay attention when you submit data to a view
scoped bean. The submitted data will "live" as long as the current view,
and will not "live" across views.
Implements Serializable
JSF and CDI managed beans should be declared Serializable (implements Serializable). This is
needed because container may persist (serialize) view data to hard disk. This
allows container to manage critical situations as heavy loading, or simply
share data with other servers in a cluster or to revive views during server
restart.
View Scope Pitfall - JSF 2.2 -
the view scoped beans and the stateless feature pitfall
In a stateless environment, the view scoped beans act as request scoped
beans. Besides the fact that you can't create/manipulate views dynamically,
this is one of the big disadvantages that comes with the stateless feature,
because it will affect AJAX-based applications that usually use view scoped
beans. You can easily test this behavior with a set of beans with different
scopes. The view scoped bean can be defined as follows:
@Named
@ViewScoped
public class
TimestampVSBean implements Serializable{
private static final long serialVersionUID =
1L;
private Timestamp timestamp;
public TimestampVSBean() {
java.util.Date date = new java.util.Date();
timestamp = new Timestamp(date.getTime());
}
public Timestamp getTimestamp() {
return timestamp;
}
public void setTimestamp(Timestamp timestamp)
{
this.timestamp = timestamp;
}
}
Just change the scope to request, session, and application to obtain
the other three beans.
Next, we will write a simple stateless view as follows:
<f:view transient="true">
<h:form>
<h:commandButton value="Generate
Timestamp"/>
</h:form>
Request Scoped Bean:
<h:outputText
value="#{timestampRSBean.timestamp}"/>
View Scoped
Bean:
<h:outputText
value="#{timestampVSBean.timestamp}"/>
[keep an eye
on this in stateless mode]
Session Scoped Bean:
<h:outputText
value="#{timestampSSBean.timestamp}"/>
Application Scoped Bean:
<h:outputText
value="#{timestampASBean.timestamp}"/>
</f:view>
Afterwards, just submit this form several times (click on the Generate Timestamp
button) and notice that the timestamp generated by the view scoped bean changes
at every request. This is a JSF pitfall!
The request, session, and application scopes work as expected!
For more view scope pitfalls, you must read: The
benefits and pitfalls of @ViewScoped,
by Bauke Scholtz (aka BalusC)
View Scope Programmatic Access
Programmatically you can interact with view scope like this:
- access the view scope map
// JSF 2.0-2.2
FacesContext
context = FacesContext.getCurrentInstance();
Map<String,
Object> viewMap = context.getViewRoot().getViewMap();
// JSF 2.3
@Inject
@ViewMap
private Map<String,
Object> viewMap;
Map<String,
Object> viewMap = Faces.getViewMap();
- set a view scoped attribute
// JSF 2.0 -
2.3
viewMap.put(name, value);
Faces.setViewAttribute(name, value);
- get a view scoped attribute
// JSF 2.0-2.3
Object value = viewMap.get(name);
<T> value = Faces.getViewAttribute(name);
- remove a view scoped attribute
// JSF 2.0-2.3
Object value = viewMap.remove(name);
<T> value = Faces.removeViewAttribute(name);
! In JSF pages, you can use the implicit
object, #{viewScope}
(e.g. get CountBean
instance: #{viewScope.countBean}).
Among others, the view map will contain instances of managed beans that
are declared under the view scope (@ViewScoped (JSF/CDI)). Practically, the view scope is
stored in the session scope.
In case of JSF managed beans (not CDI managed beans - in this case, the
keys are pretty complex), you can easily identify such beans by their names
which becomes keys in the view map. Therefore you will be able to locate an
instance of this JSF managed bean in the view map under the key, countBean. If you
specify the bean name via @ManagedBean(name="some_name"), then some_name will be the key in the view map. So, via
the view map, you can access a property of a view scoped JSF managed bean, like
this:
String count =
((CountBean)(Faces.getViewAttribute("countBean/some_name"))).getCount();
Is perfectly legal to do this also (this refers to the current bean):
@ManagedBean(name="some_name")
...
String
bean_name = getClass().getAnnotation(ManagedBean.class).name();
int count = ((CountBean)(Faces.getViewAttribute(bean_name))).getCount();
Now, you can easily intuit how to work with managed beans stored in the
view map.
Using @PostConstruct
Typically, in a managed bean, we need to write a method annotated with @PostConstruct for
accomplishing initializations tasks based on injected artifacts. With other words, the @PostConstruct annotation is used on a
method that needs to be executed after dependency injection is done to perform
any initialization. When the initialization doesn't involve injected artifacts
the constructor can be used for initializations. For view scoped beans the
method annotated with @PostConstruct
will be called only once, after the view scoped bean instance was created (the
constructor and @PostConstruct
will be invoked once per view creation).
JSF managed bean example:
import
java.io.Serializable;
import
javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
@ManagedBean
@ViewScoped
public class
InitBean implements Serializable{
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
java.io.Serializable;
import
javax.faces.bean.ManagedBean;
import
javax.faces.bean.ViewScoped;
@ManagedBean
@ViewScoped
public class
CountBean implements Serializable {
@ManagedProperty("#{initBean}")
private InitBean initBean;
@PostConstruct
public void init(){
LOG.info("CountBean#Initializing counter with @PostConstruct
...");
count = initBean.getInit();
}
public void setInitBean(InitBean initBean) {
this.initBean = initBean;
}
...
}
CDI managed bean example:
import
java.io.Serializable;
import javax.faces.view.ViewScoped;
import
javax.inject.Named;
@Named
@ViewScoped
public class
InitBean implements Serializable {
private int init;
public InitBean() {
init = 5;
}
public int getInit() {
return init;
}
public void setInit(int init) {
this.init = init;
}
}
import
java.io.Serializable;
import
javax.inject.Inject;
import javax.faces.view.ViewScoped;
import
javax.inject.Named;
@Named
@ViewScoped
public class
CountBean implements Serializable {
@Inject
private InitBean initBean;
@PostConstruct
public void init(){
LOG.info("CountBean#Initializing counter with @PostConstruct
...");
count = initBean.getInit();
}
...
}
Injection and view scoped beans
JSF: For JSF managed beans,
injection is accomplished via @ManagedProperty.
For example:
CDI: For CDI managed beans,
injection is accomplished via @Inject.
For example:
JSF & CDI mixed: CDI can
be injected in JSF (vice versa is not true!)
JSF Managed Beans Restrictions:
! As a general rule in JSF, don't
use objects that have shorter lifespan than the objects you are
calling it from. In other words, use objects whose lifespan is the same
as, or longer than, the object being injected into. Breaking this rule will end
up in a JSF exception. Base on this rule in a JSF view scoped managed bean you
can inject view, session and application managed beans, but not request managed
beans.
JSF managed beans can be injected in other JSF managed beans.
CDI Managed Beans Restrictions:
! When you are using an object
that has a shorter lifespan than the object you are calling it from (for
example, injecting a request scoped bean into a view scoped bean), CDI
classifies the use case as a mismatched injection and fixes the issue via CDI
proxies. For each request, the CDI proxy re-establishes the connection to a
live instance of the request scoped bean.
CDI managed beans can be injected in JSF managed beans.
Configuring JSF view scoped
managed beans programmatically
Starting with JSF 2.2, we can programmatically
reproduce the content of faces-config.xml.
For view scoped managed beans, the
relevant snippet of code is:
@Override
public void
populateApplicationConfiguration (Document toPopulate) {
String ns =
toPopulate.getDocumentElement().getNamespaceURI();
Element managedbeanEl =
toPopulate.createElementNS(ns, "managed-bean");
Element managedbeannameEl = toPopulate.createElementNS(ns,
"managed-bean-name");
managedbeannameEl.appendChild(toPopulate.createTextNode("countBean"));
managedbeanEl.appendChild(managedbeannameEl);
Element managedbeanclassEl =
toPopulate.createElementNS(ns, "managed-bean-class");
managedbeanclassEl.appendChild(toPopulate.createTextNode("beans.CountBean"));
managedbeanEl.appendChild(managedbeanclassEl);
Element managedbeanscopeEl = toPopulate. createElementNS(ns,
"managed-bean-scope");
managedbeanscopeEl.appendChild(toPopulate. createTextNode("view"));
managedbeanEl.appendChild(managedbeanscopeEl);
...
// programmatic create managed-property
...
toPopulate.getDocumentElement().appendChild(managedbeanEl);
}
A complete application can be seen in Mastering
JSF 2.2 book .
Configuring JSF view scoped managed
beans in XML file
With XML configuration, you can use the old JSF 1.x mechanism to define
the managed bean in a normal faces-config.xml
file. For example:
...
<managed-bean>
<managed-bean-name>countBean</managed-bean-name>
<managed-bean-class>beans.CountBean</managed-bean-class>
<managed-bean-scope>view</managed-bean-scope>
...
<!-- managed bean properties --> via <managed-property/>
...
</managed-bean>
...
Managed beans should be defined in a separate XML file because the faces-config.xml is
used to set the application level configurations. Basically, if you prefer this
approach, create a new XML file and put the managed beans detail inside.
Finally, declare the XML file via javax.faces.CONFIG_FILES context parameter in web.xml file.
...
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>WEB-INF/my-manage-beans.xml</param-value>
</context-param>
...
See you in the next post about OmniFaces view scope.
Thank you ever so for you article.Much thanks again. Want more.
RăspundețiȘtergereoracle rac online training
oracle rac training