4.4. Sorting HTML Tables

Problem

You need to sort the results of a displayed table by clicking on a column header.

Solution

Create an Action, as shown in Example 4-8, that uses the BeanComparator class of the Jakarta Commons BeanUtils library to sort the underlying Collection.

Example 4-8. Sorting tabular data with an Action

package com.oreilly.strutsckbk.ch04;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanComparator;
import org.apache.commons.collections.comparators.ReverseComparator;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class ViewForecastAction extends Action {

  public ActionForward execute(ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response) throws Exception {
    // create the weather bean
    WeeklyWeather weather = new WeeklyWeather( );        

    // create a list to hold the forecast
    List list = new ArrayList( );
    list.addAll( weather.getWeekForecast( ) );

    // get the sort by request param
    String sortBy = request.getParameter("sortBy");

    // get the reverse request param
    boolean reverse = false;
    String reverseParam = request.getParameter("reverse");
    if (reverseParam != null) 
        reverse = Boolean.valueOf(reverseParam).booleanValue( );

    // sort the list
    if (sortBy != null) {
      Comparator comparator = new BeanComparator(sortBy);
      if(reverse) comparator = new ReverseComparator(comparator);
      Collections.sort( list, comparator );
    }
    
    // add the list as a request attribute and forward to the JSP
    request.setAttribute( "weekForecast", list );
    return mapping.findForward("success");
  }
}

Then create an action element in the struts-config.xml that uses ViewForecastAction and forwards to the JSP page that displays the table:

<action path="/ViewForecast"
        type="com.oreilly.strutsckbk.ch04.ViewForecastAction">
    <forward name="success" path="/sorted_struts_table.jsp"/>
</action>

On the JSP page (sorted_struts_table.jsp), shown in Example 4-9, the table header cells contain links to the ViewForecast action. Each link sets request parameters that indicate the property name to sort by and if the sort order is reversed (i.e., descending).

Example 4-9. Using table column headers for sorting

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix=
  "bean" %>
<%@ taglib uri="http://jakarta.apache.org/struts/tags-logic" prefix=
  "logic" %>
  <html>
<head>
  <title>Struts Cookbook - Chapter 4 : Sorted Struts Table</title>
</head>
<body>
  <table width="60%" border="2">
    <tr>
      <th>Day of Week<br />
        <a href="ViewForecast.do">unsort</a>
      </th>
      <th>Chance of Precipitation<br />
        <a href="ViewForecast.do?sortBy=chancePrecip">asc</a>
        <a href="ViewForecast.do?sortBy=chancePrecip&reverse=true">desc</a>
      </th>
      <th>Expected Precipitation (inches)<br />
        <a href="ViewForecast.do?sortBy=rainfall">asc</a>
        <a href="ViewForecast.do?sortBy=rainfall&reverse=true">desc</a>
      </th>
    </tr>
    <logic:iterate id="forecast" name="weekForecast">
      <tr>
        <td>
          <bean:write name="forecast" property="day"/>
        </td>
        <td>
          <bean:write name="forecast" property="chancePrecip"/>
        </td>
        <td>
          <bean:write name="forecast" property="rainfall"/>
        </td>
      </tr>
    </logic:iterate>
  </table>
</body>
</html>

Discussion

The approach shown in the solution performs an in-memory sort of the data displayed in an HTML table.

Tip

If the tabular data were stored in a database, you would need to store the data collection in the session or refetch the data from the database between requests.

The table is sorted by creating a java.util.Comparator based on the name of the property to sort by. The BeanComparator class provides the intelligence to create the comparator using JavaBean introspection. The created Comparator sorts the data based on the natural ordering of the property. If a reverse or descending order sort is desired, a ReverseComparator is created from the BeanComparator.

The Solution for this problem is easy. The grunt work of creating the Comparators is handled by the BeanComparator. If you add a new property to the object in the collection, it can be sorted by passing in a request parameter with the name of that property. If you want to return the collection to its natural, unsorted state, don't pass in the sortBy parameter.

Sorting in ascending or descending order is handled via the reverse request parameter. This parameter is mapped to a Boolean variable. If reverse is true, a ReverseComparator is used.

The JSP page adds sorting capability by providing links that forward to the ViewForecast action. Each link specifies a sortBy parameter corresponding to the column to be sorted. The link for sorting in descending order adds the reverse parameter. Figure 4-5 shows the resultant web page for the Solution. Here the "desc" link in the third column was clicked to order the data by greatest expected rainfall. You can see the generated URL for the "desc" link in the browser's status bar.

Sorted table

Figure 4-5. Sorted table

See Also

This Solution was based in-part on suggestions made in the struts-user mailing list. One useful thread posting by Henri Yandell is archived at http://www.mail-archive.com/struts-user%40jakarta.apache.org/msg95356.html.

JSTL tags can be used to create the table instead of the Struts logic:iterate and bean:write tags. Recipe 4.3 shows examples of JSTL to create tables. Recipe 4.6 shows you how to provide table sorting using the display tags open source tag library. You do not need to code any special Java sorting actions if you use this popular tag library.

For further information on the BeanUtils sorting capabilities, check out the JavaDocs at http://jakarta.apache.org/commons/beanutils/api/org/apache/commons/beanutils/BeanComparator.html.

Get Jakarta Struts Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.