New JavaServer Faces 2.2 Feature: The viewAction Component

By Tom McGinn

The JavaServer Faces 2.2 view action feature offers developers several important advantages for building server-side user interfaces.

Published November 2011

Downloads:

Download: Sample Code (Zip)

Introduction

In this article, we look at a new feature of JavaServer Faces 2.2 that makes life easier for users of JavaServer Faces.

Note: JavaServer Faces 2.2 has not been officially released yet. You can get instructions for downloading and installing the current JavaServer Faces 2.2 code (Mojarra 2.2.0-SNAPSHOT) here. Also, the source file for the code described in this article can be downloaded here.

Two new features in the previous version, JavaServer Faces 2.0, are the bookmark ability and the ability to view parameters. These features provided a mechanism for developers to process GET requests and bind parameters passed in the request to properties in the model.

For example, Listing 1 is a simple index page that takes a catalog item id as an entry:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h=" "
      xmlns:f=" ">
    <f:metadata>
        <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    </f:metadata>
    <h:head>
        <title>Catalog Entry< /title>
    </h:head>
    <h:body>
        <h:form>
            Enter catalog number: 		
            <h:inputText id="item" value="#{catalog.item}"/>
            <h:commandButton value="submit" 
            action="catalog?faces-redirect=true&includeViewParams=true"/>
        </h:form>
    </h:body>
</html>

Listing 1. Simple Index Page

Any page that this page directs to that includes an <f:viewParam> tag can pull the parameters from the GET request into bound properties, as shown in Listing 2:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h=" "
      xmlns:f=" ">
    <h:head>
        <title>Catalog View</title>
    </h:head>
    <f:metadata>
        <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    </f:metadata>
    <h:body>
        You requested catalog item: <h:outputText value="#{catalog.item}"/>
    </h:body>
</html>

Listing 2. <f:viewParam> Tag

Suppose that before processing the item number entered on the index page, you wanted to check to see whether the item id was in a range (or some other more sophisticated value check). One way to do so is to add a validator to the index page, inside the inputText component:

Enter catalog number:
<h:inputText id="item" value="#{catalog.item}">
    <f:validator validatorId="com.jsf.sample.ValidItemRangeValidator"/>
</h:inputText>

Of course, you also need to create a class to implement  javax.faces.validator.Validator and put the logic to validate the item range into the validate method. In Listing 3, ValidItemRange is a simple bean that defines a low and high integer value range.

@FacesValidator("com.jsf.sample.ValidItemRangeValidator")		
public class ValidItemRangeValidator implements Validator, Serializable {
    @Inject private ValidItemRange range;

    @Override
    public void validate(FacesContext fc,
                         UIComponent uic,
                         Object value) throws ValidatorException {
        Integer item = (Integer)value;
        if (item.intValue() >= range.getLow() && 
            item.intValue() <= range.getHigh()) {
            return;
        }
        ((UIInput) uic).setValid(false);
        fc.addMessage(null, 
            new FacesMessage("The item number you entered is invalid."));
    } 
}

Listing 3. ValidItemRange Bean

In JavaServer Faces 2.2, there is an easier, more flexible way to perform this range check. JavaServer Faces 2.2 extends GET processing further by leveraging a feature from Seam 3: the viewAction component.

A view action operates like a button command (UICommand) component. By default, it is executed during the Invoke Application phase in response to an initial request. However, as you'll see, view actions can be invoked during any phase of the lifecycle and, optionally, during postback, making view actions well suited to performing preview checks.

Using the viewAction Component

JavaServer Faces 2.2 defines a new tag, <f:viewAction>. This tag is an ActionSource2 UIComponent that specifies an application-specific action. The viewAction component is declared as a child of the metadata facet (<f:metadata>). This allows the view action to be incorporated into the JavaServer Faces lifecycle on both non-faces (initial) and faces (postback) requests.

We can replace the validator with a much simpler view action in our catalog example, as follows:

<f:metadata>
    <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    <f:viewAction action="#{catalog.checkItem}"/>
</f:metadata>

Note: The viewAction component must be declared as a child of a metadata facet. This allows the view action to be part of the JavaServer Faces lifecycle for both non-faces and faces requests. If you put it anywhere else in the page, the behavior is undefined.

Notice that the view action calls a validation method (checkItem) of the Catalog bean, eliminating the need for a Validator class. Further, the method returns a string, which can be used for implicit navigation, as shown in Listing 4:

@Named
@RequestScoped
public class Catalog implements Serializable {

    private Integer item;
    private FacesContext facesContext;
    @Inject private ValidItemRange range; // An ApplicationScoped Bean
					            // the declares a high and low int
    @PostConstruct
    public void postConstruct() {
        facesContext = FacesContext.getCurrentInstance();
    }

    public String checkItem() {
        if (item.intValue() >= range.getLow() && 
            item.intValue() <= range.getHigh()) {
            return null;
        }
        facesContext.addMessage(null, 
            new FacesMessage("The item number you entered is invalid."));
        return "index";
    }

    // ... other getter and setter methods
}

Listing 4. Implicit Navigation

And just as with other UICommand components, the viewAction component supports declarative navigation as well. So you can write a navigation rule that is consulted before the page is rendered. If the rule matches, navigation occurs just as though this were a postback.

<navigation-rule>
    <from-view-id>/index.xhtml</from-view-id>
    <navigation-case>
        <from-action>#{catalog.checkItem}</from-action>
        <if>#{catalog.invalidItem}</if>
        <to-view-id>/index.xhtml</to-view-id>
        <redirect/>
    </navigation-case>
</navigation-rule>

By default, view actions are not executed on postback, since the viewAction component is designed to support non-faces requests. If your viewAction component is intended to be executed on both non-faces and faces requests, you can enable the view action for postback requests:

<f:viewAction action="#{catalog.checkItem}" onPostback="true"/>

The default phase for view actions is the Invoke Application phase. However, view actions in JavaServer Faces 2.2 may be invoked during any phase of the JavaServer Faces lifecycle. You can specify in which phase to execute the action using the name of the phase constant defined in the javax.faces.event.PhaseID class.

<f:viewAction action="#{catalog.checkItem}" phase="UPDATE_MODEL_VALUES"/>

In our earlier catalog example, only a few of the available phases make sense, in particular, those after the Update Model Values phase, since the application validates a user entry that is written to during this phase.

For consistency with the existing <h:commandButton> and <h:commandLink> tags, view actions can also be invoked during the Apply Request Value phase, instead of the default phase, by using the immediate attribute:

<f:viewAction action="#{catalog.buildCustomForm}" immediate="true"/>

If any phase is set, it takes precedence over the immediate attribute.

Note: View actions can be placed into a view metadata facet that has no other view parameters. While the JavaServer Faces specification requires at least one view parameter for the view metadata facet to be processed on an initial request, JavaServer Faces 2.2 relaxes that requirement.

View Action Feature Versus PreRenderViewEvent

A PreRenderViewEvent listener is another technique used in JavaServer Faces 2.0 to perform evaluations before a page is rendered. Our catalog example could check the item number for validity using a listener on an event of type preRenderView:

<f:metadata>
    <f:viewParam id="id" name="item" value="#{catalog.item}"/>
    <f:event type="preRenderView" listener="#{catalog.checkItem}"/>
</f:metadata>

However, the JavaServer Faces 2.2 view action feature provides a number of important advantages over the method of performing evaluations before a page is rendered:

  • View actions can be triggered early on, before a full component tree is built, resulting in a lighter weight call.

  • View action timing can be controlled.

  • View actions can be used in the same context as the GET request.

  • View actions support both implicit and explicit navigation.

  • View actions support both non-faces (initial) and faces (postback) requests.

A practical use for early-phase actions is a context-aware authorization check, for example, when a user of your application is trying to load a complex page, but the user is not authorized to view the page (and its content). Using a viewAction component, it is easy to evaluate (or determine) the user's credentials and navigate accordingly. This check can be made early on, rather than at the Render Response phase, preventing other (possibly costly) side effects from occurring or preventing processes from running.

Conclusion

In summary, the viewAction component simplifies the process for performing conditional checks on initial and postback requests, enables control over which phase of the lifecycle an action is performed in, and enables both implicit and declarative navigation. Give it a try!

See Also

About the Author

Tom McGinn is Principal Curriculum Developer for Oracle Server Technologies at Oracle.