Drill Down Screens with JSF

Drill Down Edit Screens with JSF

Written by Duncan Mills, Oracle Corporation
April, 2005

Defining the Problem

One of the really neat features of JavaServer Faces as a technology is the capability to very simply create multi-row editiable tables using the various dataTable components. Sometimes, however, you might want to do things in an old-fashioned way where you have a tabular view of read only data, from which you select a commandLink (hyperlink) or some other UI element to drill down into an edit screen for that particular record. An example of this is shown here:

Drilldown UI Image showing the initial tabular screen and the edit screen reached by drilling down

Fig 1: the drilldown UI

(Note that in the above figure I've superimposed the two screens for illustration. The solution as it stands will bring up the edit screen in the same window as the tabular list. A pop up window version is possible, however, if you use the ADF Faces component set)

The important thing here is that I want to use the same drilldown screen to edit or create a new record and I want to be able to choose to save my changes or cancel on navigation back from the edit/drilldown screen.

Setup

To illustrate all of this in a simple way I'll use a simple collection of Contact beans:

The Contact Bean

package com.groundside.jsf;
public class Contact {
  String _firstName;
  String _lastName;
  int    _age;
  int    _contactId = 0;

  public Contact( int    contactId,
                  String firstName,
                  String lastName,
                  int    age)
  {
    _contactId = contactId;
    _firstName = firstName;
    _lastName = lastName;
    _age = age;
  }

  /* Getters and setters for the fields */
··...
(Click here for the complete class)

Next then there is a collection of those contact objects, which for convenience also creates some sample data

The Neighbors Bean

package com.groundside.jsf;
import java.util.ArrayList;
public class Neighbors {
  ArrayList _contacts = new ArrayList();
  int       _maxContactId;

  public Neighbors()
  {
    _contacts.add(new Contact(1,"Fred","Flintstone",35));
    _contacts.add(new Contact(2,"Wilma","Flintstone",32));
    _contacts.add(new Contact(3,"Barney","Rubble",33));
    _contacts.add(new Contact(4,"Betty","Rubble",33));
    _maxContactId = 4;
  }

  public void setContacts(ArrayList contacts){...}

  public ArrayList getContacts() {...}

  public void updateContact(Contact updatedContact) {...}

  public void addContact(Contact newContact) {...}

  public void deleteContact(int contactId){...}

}

(Click here for the complete class)

The PageFlow

I just need two pages for this example, the tabular.jsp to present the summary table and the edit.jsp to edit a particular row:
JSF navigation diagram for the two sample pages

Managed Beans

Each of the pages has a request scope backing bean, additionally the Neighbors object is exposed as a session scoped bean and there is a request scope editContact managed bean of type Contact used to transfer the current row from the tabular screen to the edit screen. The complete faces-config is available here. And for reference, here are the backing beans for the two pages:

Calling the Edit screen

Getting to the edit screen with a new record is trivial, we just have to navigate there and let Faces take care of creating a new empty Contact bean for that page:

public String newButtonAction()
{
  return "drilldown";
}

Navigating to the edit page with an existing record involves getting the current row from the dataTable control. Faces will have handled setting the row currency correct based on the row you clicked on. Once we have the contact object we pre-seed the editContact managed bean with it's content:

public String editLinkAction()
{
  /* Pull out the currently selected contact */
  Contact editContact    = (Contact)this.getDataTable().getRowData();
  /* Pre-seed the managed bean */
  FacesContext ctx       = FacesContext.getCurrentInstance();
  ValueBinding binding   = ctx.getApplication().createValueBinding("#{editContact}");
  binding.setValue(ctx,editContact);
  return "drilldown";
}

Returning from the Edit Screen

On the edit screen I have two buttons:

  • A Cancel button which has a hardcoded navigation action of return, with immediate set to true.
  • A Save button which looks up the Neighbors collection and sends the changed Contact record back to it. In this implementation, the updateContact method on the Neighbors object has the smarts to work out if this is indeed an update or if this is a new record being added.

Here's the code for that Save action:

public String saveAction()
{
  FacesContext ctx               = FacesContext.getCurrentInstance();
  Application app                = ctx.getApplication();
  ValueBinding editedContactBind = app.createValueBinding("#{editContact}");
  ValueBinding contactListBind   = app.createValueBinding("#{neighbors}");
  Contact contact                =(Contact)editedContactBind.getValue(ctx);
  Neighbors neighbors            =(Neighbors)contactListBind.getValue(ctx);
  neighbors.updateContact(contact);
  return "return";
}

Summary

So that's one way to handle this scenario using raw JSF. It's quite a simple solution and nicely illustrates the use of ValueBindings to keep your code clean and uncontaminated by the servlet API.

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy