Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x

While the 2.0 release of the JSF specification will do something about it, the 1.x implementations of JavaServer Faces only offer request, session and application scope. Many JSF implementations have added additional scopes, such as the processScope in ADF Faces and the conversation scope in SEAM. Another frequently desired scope, also somewhere requestScope and sessionScope, could be called pageScope or viewScope. In this article I will describe a very simple implementation of such a scope that I needed for one of the JSF projects I am working on.

The simplified use case is the following: when I navigate to a JSF page (from another page), some fields should be reset. However, any values entered by the user in those fields should be  retained as long as the user is  staying within the page. As soon as she navigates out of the page, the values should be discarded.....

In the situation I had to deal with, the page has a search area. When the page is accessed from somewhere else, the search items should be empty. However, once the user enters search criteria and executes the query, the page returns with the search area and the search results. The search criteria entered earlier and responsible for the search results should be visible, also to allow the user to further refine them.

The items in the search area are bound to a managed bean, obviously. But what should be the scope of this bean? If the bean were requestScope, it would be reset for each request and the search criteria would disappear when the search is performed. On the other hand, with sessionScope, the search criteria would still be there when the user navigates out of the page and sometime later returns to it. So we need a mechanism that would reset the bean as soon as the user navigates out of the page, but not before. For that, I wanted the viewScope (or pageScope).

Implementing ViewScope 

The implementation is surprisingly simple – and may not be enough for all situations – yet does what I needed from it.

1. I set up a managed bean called viewScope and configure it to be in sessionScope:

<managed-bean> 
<managed-bean-name>viewScope</managed-bean-name>
<managed-bean-class>java.util.HashMap</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
 

As you can tell from this configuration snippet from the faces-config.xml, the bean is a Map – that will hold any object I put into it. 

2. Create page items bound to the viewScope bean

I create a page with the following content:

        <h:form>
<h:outputText value="Search Page"/>
<h:panelGrid columns="2">
<h:outputText value="View Scope Field"/>
<h:inputText value="#{viewScope.searchItem}"/>
<h:commandButton value="Search (stay within page)"/>
<h:commandButton value="Go away (to other page)" action="other"/>
</h:panelGrid>
</h:form>

I also create another page with almost identical content:

         <h:form>
<h:outputText value="Other Page"/>
<h:panelGrid columns="2">
<h:outputText value="View Scope Field"/>
<h:inputText value="#{viewScope.searchItem}"/>
<h:commandButton value="Search (stay within page)"/>
<h:commandButton value="Go away (to search page)" action="search"/>
</h:panelGrid>
</h:form>

Well, the main difference is the action on the second command button. Basically these two pages both contain a single inputText item, bound to the expression #{viewScope.searchItem}. Two navigation cases have been defined: from the first page to the second (action is other) and from the second to the first (action is search).

When we run the first page,

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope003 

enter a value in the search item

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope004 

and press the search button, the page will redisplay and the value entered is still there.

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope004 

 

However, when we press the Go Away button, the Other Page is displayed

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope005 

and we see the value entered on the first page – not good, as we wanted this value to be reset as soon as we navigate out of the page. When we navigate back to the search page, the value is still there, again not the functionality we intended. But of course exactly what we should have expected, given the fact that even though we called the bean viewScope, it is nothing but a session scoped thing.

What we need is a piece of logic that will reset the viewScope bean as soon as we navigate from one page to another. And that is a requirement we can easily address with a PhaseListener.

3. Create class ViewScopeManagerPhaseListener

This class kicks in before RenderResponse is performed. It looks at an entry in the viewScope with key VIEW_BASE. That value indicates the viewId (in other words: the page) for which the current edition of the viewScope was set up. If the viewId that is about to be rendered is a different one, we should reset the viewScope bean and set the VIEW_BASE entry to the viewId of this new page.

package nl.amis.jsf;

import java.util.Map;

import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

public class ViewScopeManagerPhaseListener implements PhaseListener {

private final static String VIEW_BASE_KEY = "VIEW_BASE";

public ViewScopeManagerPhaseListener() {
}

public PhaseId getPhaseId() {
return PhaseId.RENDER_RESPONSE;
}


public void beforePhase(PhaseEvent e) {
Map viewScope =
(Map)FacesContext.getCurrentInstance().getApplication().createValueBinding("#{viewScope}").getValue(FacesContext.getCurrentInstance());
String currentViewId =
FacesContext.getCurrentInstance().getViewRoot().getViewId();
if (!currentViewId.equals(viewScope.get(VIEW_BASE_KEY))) {
viewScope.clear();
viewScope.put(VIEW_BASE_KEY, currentViewId);
}
}

public void afterPhase(PhaseEvent e) {
}

}
 

4. Configure class as PhaseListener

In order to ensure that this ViewScopeManagerPhaseListener class will actually be invoked during the JSF life cycle, we have to configure it in the faces-config.xml file as a phaseListener (in JSF 2.0 we would be able to simply use an annotation in the class definition):

  <lifecycle>
<phase-listener>nl.amis.jsf.ViewScopeManagerPhaseListener</phase-listener>
</lifecycle>
 

With this in place, when we next run the application, we get the behavior we want:

< /p>

When we run the first page,

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope003 

enter a value in the search item

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope004 

and press the search button, the page will redisplay and the value entered is still there.

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope004 

 

Now, when we press the Go Away button, the Other Page is displayed

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope006 

and
the field has no value. When we press the Go away button to return to the search page, no value is displayed, as is intended.

Easy Implementation of viewScope or pageScope in JavaServer Faces (JSF) 1.x viewscope003

This implementation is fairly simplistic and I am quite sure there are many situations where it is not good enough. For example, in ADF Faces, opening a dialog window – perhaps an LOV – will reset the viewScope, what is probably not what you would want. So more logic is needed in the ViewScopeManagerPhaseListener class.