Working with Lists and Tables

These lecture notes will introduce you to some additional interface elements in JSF. Most notably, we will see how to manage lists and tables that draw their data from a database.

The example application

These notes will walk you through a simple example of a JSF application that uses JDBC to populate interface elements and perform simple tasks.

The start page of the application displays a pull-down menu of departments that appear in the course table of the student database.

Clicking the View Courses button takes the user to a page that displays information about available courses in the selected department. This information is displayed in table form, and includes links to register for individual courses.

Clicking one of the register links takes the user to a simple registration page for that course.

The start page

The start page for the application displays the list of departments for the user to select from.

<?xml version='1.0' encoding='UTF-8' ?>
<!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="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>Course Information</title>
  </h:head>
  <h:body>
    <h:form>
      <h:panelGrid columns="2">
        Select a department:
        <h:selectOneMenu value="#{courseData.department}">
          <f:selectItems value="#{courseData.allDepartments}" />
        </h:selectOneMenu>
        <h:commandButton action="courses" value="View Courses"/>
      </h:panelGrid>
    </h:form>
  </h:body>
</html>

This page is backed by a Java bean class named 'CourseData'. This bean class has a String property, 'department', and a second, read-only property, 'allDepartments'. The allDepartments property is a list property that returns a list of objects that can be used to populate the pull-down list of available departments.

The most interesting element in this page is the selectOneMenu element, which will display the list of available departments. The selectOneMenu element has a value attribute of #{courseData.department}, which specifies that the value the user selects from the menu will be placed in the department property of the courseData bean.

To specify the set of options that are available to select in the selectOneMenu, we have two options. The first option is to set up an explicit list of selectItem elements with values and labels set in the elements:

<h:selectOneMenu value="#{courseData.department}">
	<f:selectItem itemLabel="CSCI" itemValue="CSCI"/>
	<f:selectItem itemLabel="MATH" itemValue="MATH"/>
	<f:selectItem itemLabel="EDUC" itemValue="EDUC"/>
	<f:selectItem itemLabel="ITEC" itemValue="ITEC"/>
</h:selectOneMenu>

This sets up a menu of a fixed list of items. Each item will display text equal to its itemLabel attribute in the menu. If that item is selected in the menu, the value placed in the department property will come from that item's itemValue property.

The second option is to construct the menu dynamically from a list of javax.faces.model.SelectItem objects. Each SelectItem object has a Object member named 'value' and a String member named 'label'. The labels are used to populate the menu, while the value object determines what goes into the department property when the user selects that item from the menu.

<h:selectOneMenu value="#{courseData.department}">
  <f:selectItems value="#{courseData.allDepartments}" />
</h:selectOneMenu>

The list of SelectItem objects is drawn from a special read-only property of the courseData bean class, the allDepartments property. Here is the getter method for that property.

 public ArrayList<SelectItem> getAllDepartments() throws SQLException {
    ArrayList<SelectItem> result = new ArrayList<SelectItem>();

    Connection conn = ds.getConnection();

    try {
      Statement query = conn.createStatement();
      ResultSet rs = query.executeQuery("select distinct subjectId from course");

      while (rs.next()) {
        String dept = rs.getString("subjectId");
        result.add(new SelectItem(dept, dept));
      }
    } finally {
      conn.close();
    }
    return result;
  }

This method uses a DataSource member variable, ds, to get a connection to the student database and run a query that returns a list of unique department names found in the course table.

The courses page

Clicking the button in the welcome page brings the user to courses page.

<?xml version='1.0' encoding='UTF-8' ?>
<!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="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
  <h:head>
    <title>#{courseData.department} Courses</title>
  </h:head>
  <h:body>
    <h4>Available Courses in #{courseData.department}</h4>
    <h:form>
      <h:dataTable value="#{courseData.departmentCourses}" var="info"
                   frame="box" rules="all" cellpadding="4">
        <h:column>
          <f:facet name="header">
            Course Number
          </f:facet>
          <h:outputText value="#{info.number}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            Title
          </f:facet>
          <h:outputText value="#{info.title}"/>
        </h:column>
        <h:column>
          <f:facet name="header">
            Credits
          </f:facet>
          <h:outputText value="#{info.credits}"/>
        </h:column>
        <h:column>
          <h:commandLink value="Register" action="#{registration.register(info)}"/>
        </h:column>
      </h:dataTable>
    </h:form>
  </h:body>
</html>

The most interesting element in this page is the dataTable, which displays information about courses in the selected department in the form of a table. The dataTable element has a value attribute that links to another read-only property of the CourseData bean class, departmentCourses. This property is list of special bean objects that will determine the data to be displayed in the rows of the table.

The dataTable is organized heirarchically into a set of columns. Each column contains a facet that JSF will use to make the header cell for that column and an additional element that determines the cell contents to display in that column.

JSF constructs the table by iterating over the list of objects in the courseData.departmentCourses property. Each object it comes to will be linked to the info variable specified in the dataTable tag. The elements in the columns will use that info object to provide data pulled from various properties of that info object.

The objects found in the courseData.departmentCourses property are Java beans of type CourseInfo. CourseInfo is a simple bean class with properties id, number, title, and credits.

public class CourseInfo implements Serializable {

  protected int id;
  protected String number;
  protected String title;
  protected int credits;

  public CourseInfo(int id, String number, String title, int credits) {
    this.id = id;
    this.number = number;
    this.title = title;
    this.credits = credits;
  }

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getNumber() {
    return number;
  }

  public void setNumber(String number) {
    this.number = number;
  }

  public String getTitle() {
    return title;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public int getCredits() {
    return credits;
  }

  public void setCredits(int credits) {
    this.credits = credits;
  }
}

Note that this is not a managed bean class - there are no annotations here to indicate that this is a managed bean with a particular scope. Instead, this is a plain Java class. The only special requirement here is that the class be Serializable.

Here is the code for the get method for the courseData.departmentCourses property.

public ArrayList<CourseInfo> getDepartmentCourses() throws SQLException {
  ArrayList<CourseInfo> result = new ArrayList<CourseInfo>();

  Connection conn = ds.getConnection();

  try {
    Statement query = conn.createStatement();
    String qs = "select courseId,courseNumber,title,numOfCredits from course where subjectId=\'" + department + "\'";
    ResultSet rs = query.executeQuery(qs);

    while (rs.next()) {
      int id = rs.getInt("courseId");
      String number = rs.getString("courseNumber");
      String title = rs.getString("title");
      int credits = rs.getInt("numOfCredits");
      result.add(new CourseInfo(id, number, title, credits));
    }
  } finally {
    conn.close();
  }
  return result;
}

This method pulls the necessary information from the database to make a list of CourseInfo objects.

Registration links

An interesting aspect of the table in the courses page is the presence of links in the final column.

These links are command links that will take the user to the registration page for that course when clicked. Here is the code for that column of the table:

<h:column>
  <h:commandLink value="Register" action="#{registration.register(info)}"/>
</h:column>

As you can see, this is a column of commandLink elements. The action attribute of these commandLink elements links them to a register method in a Registration bean. A unique aspect of these method calls is the fact that they are parameterized method calls. Each row in the table has a CourseInfo object associated with it via the info variable. Clicking the link in a particular row calls the register method and passes it the CourseInfo object associated with that row.

Here is the code for the Registration bean class.

@ManagedBean
@SessionScoped
public class Registration {

  @Resource(name = "jdbc/course")
  private DataSource ds;
  CourseInfo info;
  int ssn;

  public Registration() {
  }

  public int getStudentID() {
    return ssn;
  }

  public void setStudentID(int id) {
    ssn = id;
  }

  public CourseInfo getCourseInfo() {
    return info;
  }

  public String register(CourseInfo info) {
    this.info = info;

    return "register";
  }

  public String recordRegistration() throws SQLException {
    String result = "error";

    Connection conn = ds.getConnection();

    try {
      PreparedStatement stmt = conn.prepareStatement("insert into enrollment(ssn,courseId) values (?,?)");
      stmt.setInt(1, ssn);
      stmt.setInt(2, info.getId());
      stmt.execute();
      result = "confirmation";
    } finally {
      conn.close();
    }
    return result;
  }
}

Note that the only thing that the register method does is to save the CourseInfo object it has been passed in a property of the Registration class and then return a navigation string telling the application to go on to the register page.

The register page

The register page gives the user the opportunity to register for the course they have selected.

Here is the code for that page.

<?xml version='1.0' encoding='UTF-8' ?>
<!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="http://java.sun.com/jsf/html">
  <h:head>
    <title>Register for #{registration.courseInfo.title}</title>
  </h:head>
  <h:body>
    Enter your student ID number to register for #{registration.courseInfo.title}.
    <h:form>
      <h:panelGrid columns="2">
        Student ID:
        <h:inputText value="#{registration.studentID}"/>
      </h:panelGrid>
      <h:commandButton value="Register" action="#{registration.recordRegistration}"/>
    </h:form>
  </h:body>
</html>

Note that on the previous page we set the courseInfo property of the registration bean to give us course information for the course the user wants to register for. The only thing left to do here is to get the users student id, store it in the studentID property of the registration bean, and then run the recordRegistration method when the user clicks the Register button.

That method simply adds an entry to the enrollment table of the student database and then navigates the user to the confirmation page. The identifying number we need for the course in question is already embedded in the CourseInfo object in the info property.

public String recordRegistration() throws SQLException {
  String result = "error";

  Connection conn = ds.getConnection();

  try {
    PreparedStatement stmt = conn.prepareStatement("insert into enrollment(ssn,courseId) values (?,?)");
    stmt.setInt(1, ssn);
    stmt.setInt(2, info.getId());
    stmt.execute();
    result = "confirmation";
  } finally {
    conn.close();
  }
  return result;

}