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

Lucas Jellema
0 0
Read Time:6 Minute, 4 Second

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,

 

enter a value in the search item

 

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

 

 

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

 

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,

 

enter a value in the search item

 

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

 

 

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

 

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.

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.

About Post Author

Lucas Jellema

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director and Oracle Developer Champion. Solution architect and developer on diverse areas including SQL, JavaScript, Kubernetes & Docker, Machine Learning, Java, SOA and microservices, events in various shapes and forms and many other things. Author of the Oracle Press book Oracle SOA Suite 12c Handbook. Frequent presenter on user groups and community events and conferences such as JavaOne, Oracle Code, CodeOne, NLJUG JFall and Oracle OpenWorld.
Happy
Happy
0 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
Next Post

The SOA case of the missing bag - a Service Oriented Analysis

Though I did not particularly liked it, not having my bag returned by the conveyor belts of New Orleans Airport as I arrived yesterday for the ODTUG 2008 Kaleidoscope conference only slightly dented my good spirits. And after a short night’s sleep I saw the Service Oriented side of things. […]
%d bloggers like this: