Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Define a custom tag for table display

Save yourself time and create more manageable Web apps

Photo of Srinivasa Rao Karanam
Srinivasa has more than five years of experience in JavaEE-related technologies. He joined IBM Global Services, India in 2004. Srinivasa currently works on BizPortlets, which are sample portlets developed for the WebSphere Portal Server.

Summary:  Create more manageable Web apps and make code updates in one place. When you define a custom tag for tables that display in multiple places, the Tag class reflects any changes across all JSP pages. Learn to create just such a flexible tag -- with various HTML controls, including href, checkbox, radio button, text field, and combo box.

Date:  23 Aug 2005
Level:  Intermediate

Activity:  5368 views
Comments:  

Define a custom tag for table display and make your Web applications easier to manage. You'll reduce the overhead of writing the same piece of code multiple times because you update the code in only one place. The Tag class reflects your changes across all JSP pages. Here, I'll explain how to create a custom tag for table display that is flexible enough to handle different HTML controls, such as href, checkbox, radio button, text field, and combo box.

What is a custom tag?

Custom tags are user-defined JSP language elements. When a JSP page that contains a custom tag translates into a servlet, the tag converts to operations (doStartTag() and doEndTag()) on an object, called a tag handler. The Web container then invokes those operations when the JSP's servlet executes.

Custom tags have a rich set of features. They can:

  • Access all the objects available to JSP pages.
  • Modify the response that the calling page generates.
  • Communicate with each other. You can create and initialize a JavaBeans component, create a variable that refers to that bean in one tag, and then use the bean in another tag.

You can also:

  • Customize the tags through attributes passed from the calling page
  • Nest them within one another, allowing for complex interactions within a JSP page

Some advantages of custom tags are:

  1. Page designers can abstract from a complex set of logic. While the JSP page that references the tag executes, the JSP runtime invokes the tag handler class to execute the business logic it defines.
  2. You can use a custom tag on multiple places simply by invoking the tag, and remove the repetition of writing the same piece of code in different places.
  3. For any future code changes, you just make the change in one place; in the Tag class, which reflects the changes across all applicable JSP pages.

Suppose that the application development of a mutual fund system requires over 100 JSP pages, and most of the pages need table data. However, not all JSP pages require the same type of table data. In that case, you want to design a custom tag that supports all these table types.

When you design the table tag, users should be able to specify the following:

  • Width of a table column
  • Link (href) for a table column
  • Checkbox or radio button as column values
  • How values align (left/right)
  • Text fields (for user input)
  • Combo box as column data
  • Navigation for table data (Prev/Next)

Implementation

You will use the jspTableID key to uniquely identify the table tag entries for a particular JSP page. You can define any value but make sure it uniquely identifies the particular table.

Table tag uses two property files -- TableHeaderDisplay.properties and TableValueDisplay.properties -- to specify table header values and data values.

TableHeaderDisplay.properties defines the column header names, column width, and control names for the jspTableID key. This is the sample property entry in TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
   Employee No:150:href&First Name:210&Last Name:150
   &Dept:150&Job:60

  • SearchEmployee.jsp&EmployeeSearchResults is the jspTableID.
  • Employee No, First Name, Last Name, Dept, and Job are the column header names. 150, 210, 150, 150, and 60 are corresponding column widths.
  • The href for column Employee No specifies a link to the Employee No. column.

TableValueDisplay.properties defines the value objects to the jspTableID key. This is the sample property entry in TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

 SearchEmployee.jsp&EmployeeSearchResults = 
  EmployeeSearchResults@com.ibm.optimistic.model.bo.Employee@EmpNo
  &FirstName&LastName&Dept&Job 

  • SearchEmployee.jsp&EmployeeSearchResults is the jspTableID.
  • EmployeeSearchResults is the session attribute name, where the rows of records are stored for table display.
  • com.ibm.optimistic.model.bo.Employee is the value object to hold data for each row.
  • EmpNo&FirstName&LastName&Dept&Job are the variable names defined in the value object.

The following code for a JSP page invokes the table tag to display the Employee table (single line of code displayed on multiple lines for viewing convenience):

<tblTag:tableBuilder 
  jspTableID="SearchEmployee.jsp&EmployeeSearchResults"> 
</tblTag:tableBuilder> 

Define a tld file for the table tag

A tld file defines one tag called tableBuilder. The table tag's core logic is defined in the com.ibm.optimistic.tags.TableTag class. The tag has one mandatory attribute: jspTableID. You must use this attribute to identify the table uniquely.

The tag has two optional attributes: paginate and formName. The paginate attribute determines whether or not pagination is required. If paginate equals true, the table displays the Prev/Next buttons. You use the formName attribute to set the name of the form that contains a table. The attribute takes the default value as forms[0].


Listing 1. Define a tableBuilder tag
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>customtags</shortname>
<uri>/WEB-INF/taglib.tld</uri>
<tag>
  <name>tableBuilder</name>
  <tagclass>com.ibm.optimistic.tags.TableTag</tagclass>
  <attribute>
    <name>jspTableID</name>
    <required>true</required>
  </attribute>
  <attribute>
    <name>paginate</name>
    <required>false</required>
  </attribute>
  <attribute>
    <name>formName</name>
    <required>false</required>
  </attribute>
</tag>
</taglib>

The TableTag class

As I mentioned, we will define the table tag's core logic in the TableTag class. To implement this class, you first create the JspWriter object to send the HTML content to the browser.

javax.servlet.jsp.JspWriter obj_JspWriterOutToBrowser = null;
obj_JspWriterOutToBrowser = pageContext.getOut();

The following code block:

  1. Gets the Locale object.
  2. Reads the property files TableHeaderDisplay.properties and TableValueDisplay.properties.
  3. Reads the header and value entries, based on the jspTableID.
  4. Obtains the reference for the table ID, value object class, session attribute for data rows, and session attribute for combo box if one exists.
  5. Parses the property entry and stores it in a hashtable for future reference.

Listing 2. Define core logic in TableTag class
/** Assigning the default lang */
 obj_LocaleGetDefault = Locale.getDefault();

 /** loading the TableHeaderDisplay properties file*/
 obj_ResourceBundleHeaderProperty = ResourceBundle.getBundle(
        HEADER_PROPFILE,obj_LocaleGetDefault);

 StringTokenizer obj_StringTokenizerTableKey = 
        new StringTokenizer(sJspTableID,"&");
  obj_StringTokenizerTableKey.nextToken();
  String sTableID = obj_StringTokenizerTableKey.nextToken();

  /** loading the TableValueDisplay properties file*/
  obj_ResourceBundleValueProperty = ResourceBundle.getBundle(
        VALUE_PROPFILE,obj_LocaleGetDefault);
  /**
  Reading the Header property by passing its respective key 
  as an attribute
  */
  String sGetColumnHeader =
        obj_ResourceBundleHeaderProperty.getString(sJspTableID);
  /**
  Reading the TableValue property by passing its respective key 
  as an attribute
  */
    String sGetVOClassName =
        obj_ResourceBundleValueProperty.getString(sJspTableID);
    /** passing the TableValue property as an argument */
    StringTokenizer obj_StringTokenizer = 
         new StringTokenizer(sGetVOClassName,"@");
/** get the vector attribute from the tokenizer and assign it 
to string 
*/
    String sSessionAttribute = obj_StringTokenizer.nextToken();
/** get the ValueObject name from the tokenizer and assign it 
to string
*/
   String sValueObject= obj_StringTokenizer.nextToken();
   /**
   get all method names as single token from the tokenizer 
   and assign it to string
   */
   String sValueObjectMethods= obj_StringTokenizer.nextToken();

   String sComboAttrib = new String(" ");
   Hashtable  obj_HashtableColumnHeader = new Hashtable();
StringTokenizer obj_StringTokenizerColumnValue = 
     new StringTokenizer(sGetColumnHeader,"&");

   int iColumnNumber =0;
char[] cCSS = new char[obj_StringTokenizerColumnValue.countTokens()];

   for (iColumnNumber = 1; 
      obj_StringTokenizerColumnValue.hasMoreTokens();iColumnNumber++) {
     StringTokenizer obj_StringTokenizerColumnSubValue = null;
     obj_StringTokenizerColumnSubValue = new StringTokenizer
     (obj_StringTokenizerColumnValue.nextToken(),":");
    for(int iParaNumber = 
1;obj_StringTokenizerColumnSubValue.hasMoreTokens();iParaNumber++) {
       String sHtKey = "col" + iColumnNumber + "N" + iParaNumber;
       String sHtString = obj_StringTokenizerColumnSubValue.nextToken();

         if(sHtString != null) {
         if(sHtString.equalsIgnoreCase("combo")) {
            StringTokenizer sCombo = 
                  new StringTokenizer(sSessionAttribute,":");
            sSessionAttribute = sCombo.nextToken();
            sComboAttrib = sCombo.nextToken();
           }
         }

       obj_HashtableColumnHeader.put(sHtKey,sHtString);

     } // END OF INNER FOR LOOP

   } // END OF OUTER FOR LOOP

If the attribute bPaginate is true, then the table displays the Prev/Next buttons.

if(bPaginate==true){
obj_JspWriterOutToBrowser.println("<input type ='button' class = 
        'BUTTON' "+ "name ='Prev' value='Previous' 
        onclick=setTableTagMode('Prev')>");
obj_JspWriterOutToBrowser.println("<input type ='button' class = 
        'BUTTON' "+ "name ='Next' value='Next' 
        onclick=setTableTagMode('Next')>");
    obj_JspWriterOutToBrowser.println("<br>");
}

To create the HTML tag for table display, use:

obj_JspWriterOutToBrowser.println("<table border='1' cellpadding='0' "+
        "width=\"750\" cellspacing='0' name='"+sTableID+"'  
        id='"+sTableID+"'>");

The code in Listing 3 creates the table header. It parses the table header property entry, gets the column name details, and aligns the flag (left/right) for displaying the table data.


Listing 3. Create the table header
obj_JspWriterOutToBrowser.println("<tr>");
StringTokenizer obj_StringTokenizerColumnHeader =
        new StringTokenizer(sGetColumnHeader,"&");
obj_ArrayListAlignFlag = new java.util.ArrayList(
        obj_StringTokenizerColumnHeader.countTokens());

while (obj_StringTokenizerColumnHeader.hasMoreTokens())      {
String sColumnToken = obj_StringTokenizerColumnHeader.nextToken();
        StringTokenizer sColumnSubToken = 
                        new StringTokenizer(sColumnToken,":");
        while (sColumnSubToken.hasMoreTokens()) {
String sColumnHeader    = sColumnSubToken.nextToken();
               StringTokenizer sColumnSub =
                   new StringTokenizer(sColumnHeader," ");
               StringBuffer sColumnHead = new StringBuffer();
               String sTemp;
               while (sColumnSub.hasMoreTokens()) {
                   sTemp = sColumnSub.nextToken();
                   if(sTemp.equalsIgnoreCase("<r>"))
                   {
                       sAlignFlag="r";
                   }
                   else
                   {
                       sColumnHead.append(sTemp);
                       sColumnHead.append("&nbsp;");
                   }
               }
               String sWidthSize = sColumnSubToken.nextToken();
               if(sAlignFlag.equalsIgnoreCase("r"))
               {
                   obj_ArrayListAlignFlag.add("r");
               }
               else
               {
                   obj_ArrayListAlignFlag.add("l");
               }
               iNoCols++;
               
               /****** displays Table Column Headers *****/
               obj_JspWriterOutToBrowser.println("<td "+
                   "class='columnLabelString' "+
                   "height='20' width="+sWidthSize +
                   " align='center'  nowrap>"+
                   sColumnHead.toString()+"</td>");
                   break;
        }
}
obj_JspWriterOutToBrowser.println("</tr>");

Next, you get the session reference, and then retrieve the table data (vector of value objects) and combo box data (vector of strings) from the session.

/**  gets HttpSession object from pageContext */
javax.servlet.http.HttpSession obj_HttpSessionVecOfValueObject = 
        pageContext.getSession();

    /** Retrieves vector of value objects from session */
    obj_VectorValueObject =
(Vector)obj_HttpSessionVecOfValueObject.getAttribute(sSessionAttribute);
    /** Retrieves vector of strings from session */
Vector obj_VectorObject = (Vector) 
        obj_HttpSessionVecOfValueObject.getAttribute(sComboAttrib);

To calculate startRecord and endRecord to be displayed on the page, you:

  1. Load the ValueObject class.
  2. Check for pagination. If pagination is enabled, then calculate the starting value based on the Prev/Next mode the user selects.
  3. For the Next button, StartValue = startValue from Session+Number of records per page.
  4. LoopCounter holds the last record to be displayed on the page.
  5. For nonpagination display, startValue = 0, and LoopCounter equals the size of the vector holding the table data.

In Listing 4, you can see how it works:

Next, you retrieve the values from the value object and store them in a vector.

  1. The tag handler class iterates on the vector of value objects. For each value object, the values are stored in the inner vector. The outer vector holds all the inner vector objects.
  2. Using the Reflection API, you obtain each variable's value and store it in the inner vector.

Listing 5. Retrieve values from the value object and store them in a vector
/* outer for loop starts */
  for (int i = 0; i < obj_VectorValueObject.size(); i++) {
    /** gets the valueobject from the vector */
    Object obj_ObjectVO = obj_VectorValueObject.elementAt(i);
    /**
    passing the getter methods as an argument 
    to the StringTokenizer constructor
    */
    StringTokenizer obj_StringTokenizerVOMethods =
            new StringTokenizer(sValueObjectMethods, "&");
    /** This String object gets the value from the value object */
    String sMethodValue = null;
    Vector obj_VectorInnerVector = new Vector();

    /* inner for loop starts */
    for (int k = 0;
      obj_StringTokenizerVOMethods.hasMoreTokens();
      k++) {
      String sFieldName =
            obj_StringTokenizerVOMethods.nextToken().trim();
      /**
      check the field names, if field name is not a control, 
      invoke the method; 
      otherwise, add a dummy string to the inner vector
       */
      if (!sFieldName.equals("control")) {
        /**
        Get the VO field names from the TableValueDisplay 
        property file and pass it as an argument to getMethod()of class 
        object, which returns the java.lang.reflect.Method object
        */
        Method obj_MethodDynamic =
            obj_ClassDynamic.getMethod("get" + sFieldName, null);
        cCSS[k] = sFieldName.charAt(0);

        /**
        the getter methods are called dynamically using
        the invoke method of java.lang.reflect.Method object
        */

        Object obj_ObjectVOValue =
            (obj_MethodDynamic.invoke(obj_ObjectVO, null));
        /**
        if obj_ObjectVOValue is null, then assign a namespace value 
        to the string object; otherwise, convert the object value 
        to string and assign it to sMethodValue variable
        */
        if (obj_ObjectVOValue == null
            || (obj_ObjectVOValue.toString()).trim().equals(""))
            sMethodValue = "&nbsp;";
        else
            sMethodValue = obj_ObjectVOValue.toString();

      } //end for control check

      /**
      if the field is a control, add a dummy string 
      to the inner vector 
      */
      if (obj_HashtableColumnHeader.get("col" + (k + 1) + "N" + 3)
        != null) {
        if (!(obj_HashtableColumnHeader
            .get("col" + (k + 1) + "N" + 3)
            .equals("href"))) {
            /** TextBox starts */
            if (sFieldName.equals("control")) {
                obj_VectorInnerVector.addElement("");
            } else {
                obj_VectorInnerVector.addElement(sMethodValue);
            }
        } else {
               obj_VectorInnerVector.addElement(sMethodValue);

            }
      } else {
            obj_VectorInnerVector.addElement(sMethodValue);
      }
    } // inner for loop ends

    /** vector, which contains values to be displayed 
    in the table, is added into another vector for simplicity */
    obj_VectorOuterVector.addElement(obj_VectorInnerVector);
  } // end of outer for loop

Then you print this JavaScript function at client side to handle the radio button.


Listing 6. JavaScript function for radio button
  obj_JspWriterOutToBrowser.println("<script language='JavaScript'>");
    obj_JspWriterOutToBrowser.println(
       "function " + "handleTableRadioCtrl(linkIndex) {");
    if (bFlag)
       obj_JspWriterOutToBrowser.println(
         "document." + sFormName 
              + ".iTableLink.value = linkIndex;}");
    else
       obj_JspWriterOutToBrowser.println(
          "document.forms["
              + iFormNumber
              + "].iTableLink.value = linkIndex;}");

   obj_JspWriterOutToBrowser.println("</script>");

Next, print this JavaScript function at client side to handle the hyperlink.


Listing 7. JavaScript function for hyperlink
  obj_JspWriterOutToBrowser.println("<script language='JavaScript'>");
  obj_JspWriterOutToBrowser.println(
    "function " + "handleTableLink(linkIndex) {");
  if (bFlag) {
    obj_JspWriterOutToBrowser.println(
       "document." + sFormName + ".iTableLink.value = linkIndex");
    obj_JspWriterOutToBrowser.println(
       "document." + sFormName + ".jspMode.value = 'Link'");
    obj_JspWriterOutToBrowser.println(
       "document." + sFormName + ".submit()}");
  } else {
    obj_JspWriterOutToBrowser.println(
       "document.forms["
                   + iFormNumber
                   + "].iTableLink.value = linkIndex");
    obj_JspWriterOutToBrowser.println(
       "document.forms[" 
                   + iFormNumber 
                   + "].jspMode.value = 'Link'");
    obj_JspWriterOutToBrowser.println(
       "document.forms[" + iFormNumber + "].submit()}");
  }
  obj_JspWriterOutToBrowser.println("</script>");

The table column value starts processing here. The code iterates on the outer vector; for each inner vector, it creates a <tr> element. While constructing <tr>, the code performs different checkups for different controls.


Listing 8. Process the table column value
  if (obj_VectorOuterVector.size() != 0) {
    for (int iNoOfRows = iStartValue;
      iNoOfRows < iLoopCounter;
      iNoOfRows++) {
      Vector obj_VectorOneRowValue =
          (Vector) obj_VectorOuterVector.elementAt(iNoOfRows);

      /*******Table Row Value Starts****************/
      obj_JspWriterOutToBrowser.println("<tr>");
      for (int iColumn = 0;
          (iColumn < obj_VectorOneRowValue.size());
          iColumn++) {
          if !(obj_HashtableColumnHeader
              .get("col" + (iColumn + 1 "N" + 3) == null)) {
              String strColumnType =
                  (String) obj_HashtableColumnHeader.get(
                      "col" + (iColumn + 1) + "N" + 3);

          }

      }

To create a textbox as your table column:


Listing 9. Create a textbox as the table column
  if ((strColumnType.toLowerCase()).startsWith("textbox")) {
    sMaxLength = "20";
    if (strColumnType.trim().length() > 7) {
       sMaxLength =
         strColumnType.substring(7, strColumnType.trim().length());
    }
    String textFiledSizeIE = "";
    String textFiledSizeNS = "";
    try {
        textFiledSizeIE =
            "" + ((new Integer(sMaxLength).intValue()) + 1);
        if ((new Integer(textFiledSizeIE).intValue()) > 7) {
            textFiledSizeNS = 
               "" + ((new Integer(textFiledSizeIE).intValue()) / 2);
        } else {
            textFiledSizeNS = textFiledSizeIE;
        }
    } catch (NumberFormatException obj_NumberFormatException) {
        textFiledSizeIE = "20";
        textFiledSizeNS = "10";
    }
    obj_JspWriterOutToBrowser.println(
        "<td  height=\"15\" align=\"center\" width=\"40\">"
	               + "<script>\n if(document.layers) \n "
	               + " document.write(\" "
	               + "<input class='formBox' type=text size="
	               + textFiledSizeNS
	               + " maxlength='"

	               + sMaxLength
	               + "' value='"
	               + obj_VectorOneRowValue.elementAt(iColumn)
	               + "'  name="
	               + obj_HashtableColumnHeader.get(
	                   "col" + (iColumn + 1) + "N" + 4)
	               + ">\"); \n"
	               + " else \n "
	               + " document.write(\" "
	               + "<input class='formBox' type=text size="
	               + textFiledSizeIE
	               + " maxlength='"
	               + sMaxLength
	               + "' value='"
	               + obj_VectorOneRowValue.elementAt(iColumn)
	               + "'  name="
	               + obj_HashtableColumnHeader.get(
	                   "col" + (iColumn + 1) + "N" + 4)
	               + ">\"); \n"
	               + "</script> "
	               + "</td>");
  }

Alternatively, to create a checkbox as your table column:


Listing 10. Create a check box as the table column
/**  for CheckBox  **/
  if (strColumnType.equalsIgnoreCase("CheckBox")) {
    chkField =
        (String) obj_HashtableColumnHeader.get(
                       "col" + (iColumn + 1) + "N" + 4);
    obj_JspWriterOutToBrowser.println(
        "<td  height=\"15\" align=\"center\" "
                       + " nowrap>"
                       + "<input  tabindex='2' "
                       + " type=checkbox "
                       + " name="
                       + chkField.trim()
                       + " onClick=\"javascript:chkStatus"
                       + "(this.form."
                       + (chkField.trim())
                       + iChkElementCount
                       + ".value,this.checked)\">"

                       + "<input type=hidden name="
                       + chkField.trim()
                       + iChkElementCount
                       + "  value="
                       + iChkElementCount++
                       + " >"
                       + " </td>");
    obj_JspWriterOutToBrowser.println(
        "<input type=hidden name=hd"
                       + chkField.trim()
                       + "  value='off'>");

  }

To create a radio button as your table column:


Listing 11. Create a radio button as the table column
  if (strColumnType.equalsIgnoreCase("Radio")) {
      obj_JspWriterOutToBrowser.println(
           "<td class=radioButton "
               + "height=\"15\" align=\"center\" >"
               + " <input type=radio name="
               + obj_HashtableColumnHeader.get(
                   "col" + (iColumn + 1) + "N" + 4)
               + " onClick=javascript:handleTableRadioCtrl('"
               + ilinkIncrement++
               + "')></td>");
      iRadioButtonIncrement++;
    }

To create a button as your table column:


Listing 12. Create a button as the table column
  if (strColumnType.equalsIgnoreCase("Button")) {
      obj_JspWriterOutToBrowser.println(
           "<td width=\"63\" "
               + "height=\"15\" align=\"center\" width=\"50\">"
               + " <input type=button name="
               + obj_HashtableColumnHeader.get(
                   "col" + (iColumn + 1) + "N" + 4)
               + " value = "
               + obj_HashtableColumnHeader.get(
                   "col" + (iColumn + 1) + "N" + 5)
               + " onClick=javascript:handleTableButtonCtrl('"
               + iButtonIncrement++
               + "')></td>");

    }

To create a combo box as your table column:


Listing 13. Create a combo box as the table column
if (strColumnType.equalsIgnoreCase("Combo")) {
    StringTokenizer obj_StringTokenizerCombo = null;
    obj_JspWriterOutToBrowser.println(
        "<td width=\"50\" "
            + " align=\"center\"  height=\"24\"><select "
            + " class=ListString name="
            + obj_HashtableColumnHeader.get(
                    "col" + (iColumn + 1) + "N" + 4)
            + " id="
            + obj_HashtableColumnHeader.get(
                    "col" + (iColumn + 1) + "N" + 4)
            + ">");

    for (int i = 0; i < obj_VectorObject.size(); i++) {
        obj_StringTokenizerCombo =
            new StringTokenizer(
                 (String) obj_VectorObject.elementAt(i),
                 "&");
        String sComboValue = obj_StringTokenizerCombo.nextToken();
        String sComboValueFromVec =
                (String) obj_VectorOneRowValue.elementAt(iColumn);
        String sComboDisplay = 
                obj_StringTokenizerCombo.nextToken();
        if (sComboValue
            .trim()
            .equalsIgnoreCase(sComboValueFromVec.trim())
            || sComboDisplay.trim().equalsIgnoreCase(
                        sComboValueFromVec.trim())) {
            obj_JspWriterOutToBrowser.println(
                        "<option value="
                                    + sComboValue
                                    + " selected>"
                                    + sComboDisplay
                                    + "</option>");

        } else {
            obj_JspWriterOutToBrowser.println(
                        "<option "
                                    + "value="
                                    + sComboValue
                                    + ">"
                                    + sComboDisplay
                                    + "</option>");
        }

    }
    obj_JspWriterOutToBrowser.println("</select></td>");
  }

Finally, to create an href link as your table column:


Listing 14. Create an href link as the table column
  if (strColumnType.equalsIgnoreCase("href")) {
    String sHrefLink =
         (String) obj_VectorOneRowValue.elementAt(iColumn);
    obj_JspWriterOutToBrowser.println(
         "<td tabindex='1' align=\"center\" class=link  height=\"15\" "
	               + ilinkIncrement++
	               + "')>"
	               + sHrefLink
	               + "</a></td>");

 }

The tag handler class prints this JavaScript function at client side to handle checkbox values:


Listing 15. JavaScript function to handle checkbox values
  if (chkField != null) {
    if (bFlag) {
      obj_JspWriterOutToBrowser.println(
          "<script language=javascript> ");
      obj_JspWriterOutToBrowser.println(
          "document.body.onunload=deselect;");
      obj_JspWriterOutToBrowser.print("var ichkElementLength=");
      obj_JspWriterOutToBrowser.print(
          "document."
              + sFormName.trim()
              + "."
              + chkField.trim()
              + ".length;\n");
      obj_JspWriterOutToBrowser.println(
          "function deselect()\n{\nfor(i=0;i<ichkElementLength;i++)\n");
      obj_JspWriterOutToBrowser.println(
          "{ \n if(document."
              + sFormName.trim()
               "."
               + chkField.trim()
               + "[i].checked)");
      obj_JspWriterOutToBrowser.println(
          "{\ndocument."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + "[i].value='on';}}}");
      obj_JspWriterOutToBrowser.println(
          " function chkStatus(val,bool)\n");
      obj_JspWriterOutToBrowser.println(
          "{\n if(!document.layers) \n if(document."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + ".length)");
      obj_JspWriterOutToBrowser.print(
          "\n{\ndocument."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + "[val].");
      obj_JspWriterOutToBrowser.print(
          "value= bool ?'on':'off';\n}\nelse\n{\n");
      obj_JspWriterOutToBrowser.print(
          "document."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + ".value=");
      obj_JspWriterOutToBrowser.print("bool ?'on':'off';\n}\n ");
      obj_JspWriterOutToBrowser.println(
          "\n if(document.layers) \n if((\"\"+document."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + ").indexOf(\"object InputArray\") != -1 ) ");
      obj_JspWriterOutToBrowser.print(
          "\n{\ndocument."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + "[val].");
      obj_JspWriterOutToBrowser.print(
          "value= bool ?'on':'off';\n}\nelse\n{\n");
      obj_JspWriterOutToBrowser.print(
          "document."
              + sFormName.trim()
              + ".hd"
              + chkField.trim()
              + ".value= bool ?'on':'off';");
      obj_JspWriterOutToBrowser.print("} } \n</script>\n");
    } else {
      obj_JspWriterOutToBrowser.println(
          "<script language=javascript>\n ");
      obj_JspWriterOutToBrowser.println(
          "document.body.onunload=deselect;");
      obj_JspWriterOutToBrowser.print("var ichkElementLength=");
      obj_JspWriterOutToBrowser.print(
          "document.forms["
              + iFormNumber
              + "]."
              + chkField.trim()
              + ".length;\n");
      obj_JspWriterOutToBrowser.print(
          "function deselect()\n{\nfor(i=0;i<ichkElementLength;i++)\n");
      obj_JspWriterOutToBrowser.print(
          "{\n  if(document.forms["
              + iFormNumber
              + "]."
              + chkField.trim()
              + "[i].checked)\n");
      obj_JspWriterOutToBrowser.print(
          "{\ndocument.forms["
              + iFormNumber
              + "].hd"
              + chkField.trim()
              + "[i].value='on';}}}");
      obj_JspWriterOutToBrowser.print(
          " function chkStatus(val,bool)\n");
      obj_JspWriterOutToBrowser.print(
          "{\nif(document.forms["
              + iFormNumber
              + "].hd"
              + chkField.trim()
              + ".length)\n");
      obj_JspWriterOutToBrowser.print(
          "{\ndocument.forms["
              + iFormNumber
              + "].hd"
              + chkField.trim()
              + "[val].");
      obj_JspWriterOutToBrowser.print(
          "value= bool ?'on':'off';\n}\nelse\n{\n");
      obj_JspWriterOutToBrowser.print(
          "document.forms["
              + iFormNumber
              + "].hd"
              + chkField.trim()
              + ".value=");
      obj_JspWriterOutToBrowser.print(
          "bool ?'on':'off';\n}\n}\n</script>");
    }
  }

The tag handler prints this JavaScript function at client side to handle the Previous and Next buttons.


Listing 16. JavaScript function to handle Previous and Next buttons
  if (bPaginate == true) {
    obj_JspWriterOutToBrowser.println("<script language='JavaScript'>");
    obj_JspWriterOutToBrowser.println(
         "function setTableTagMode(sMode){");
    if (iStartValue == 0) {
        obj_JspWriterOutToBrowser.println("if(sMode=='Prev'){");
        obj_JspWriterOutToBrowser.println(
            "alert('There are no previous records');");
        obj_JspWriterOutToBrowser.println("return;}");
    }
    if (iLoopCounter >= obj_VectorOuterVector.size()) {
        obj_JspWriterOutToBrowser.println("if(sMode=='Next'){");
        obj_JspWriterOutToBrowser.println(
            "alert('There are no more records');");
        obj_JspWriterOutToBrowser.println("return;}");
    }
    if (bFlag) {
        obj_JspWriterOutToBrowser.println(
            "document." + sFormName + ".jspMode.value = sMode");
        obj_JspWriterOutToBrowser.println(
            "document." + sFormName + ".submit();}");
    } else {
        obj_JspWriterOutToBrowser.println(
            "document.forms["
                    + iFormNumber
                    + "].jspMode.value = sMode");
        obj_JspWriterOutToBrowser.println(
            "document.forms[" + iFormNumber + "].submit();}");
    }

    obj_JspWriterOutToBrowser.println("</script>");
  }


Sample screen captures for a table

Note: In your JSP page, include the hidden parameter jspMode to set the action name. The request processing class must set the action name to the jspMode session variable so the table tag can use it to track user actions.

<input type="hidden" name="jspMode" >

The request processing class also must set the session variable jspMode with the parameter value read from the request object.

if (request.getParameter("jspMode") != null){
request.getSession().setAttribute("jspMode", 
request.getParameter("jspMode"));
}

To create a simple table display for viewing search results, use the following property files:

TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
Employee No:150&First Name:210&Last Name:150&Dept:150&Job:60

TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
EmployeeSearchResults@com.ibm.optimistic.model.bo.Employee@EmpNo
&FirstName&LastName&Dept&Job

Figure 1 illustrates this view.


Figure 1. Search results
Search results view

To create a table display with href as the column type, use these property files:

TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
Employee No:150:href&First Name:210&Last Name:150
&Dept:150&Job:60

TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
EmployeeSearchResults@com.ibm.optimistic.model.bo.Employee@EmpNo
&FirstName&LastName&Dept&Job

When you display a table with an href column (see Figure 2), the HeaderDisplay property contains the Employee No:150:href entry to set a link to the Employee No. column.

The user clicks the href link to call the following JavaScript function:

<script language='JavaScript'>
function handleTableLink(linkIndex) {
document.forms[0].iTableLink.value = linkIndex
document.forms[0].jspMode.value = 'Link'
document.forms[0].submit()}
</script>

For Employee No:111111, handleTableLink(0) calls the above function. The function sets the form variable iTableLink with an index of the selected href link, and the jspMode identifies the user action, such as save, update, or delete. To get the details of the corresponding href, use the iTableLink value. The iTableLink represents the index of the value object. (The table data comes from the session; it is stored in the session with the vector of value objects).


Figure 2. Table with href column
Table with href column

To create a table display with a checkbox as the column type, use the following property files.

TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
Employee No:150&First Name:210&Last Name:150
&Dept:150&Job:60&Delete:20:checkbox:chkDelete

TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
EmployeeSearchResults@com.ibm.optimistic.model.bo.Employee@EmpNo
&FirstName&LastName&Dept&Job&control

The HeaderDisplay property contains the Delete:20:checkbox:chkDeleteentry to display the checkbox.

The user clicks the checkbox to call the following JavaScript function. The function sets the hdchkDelete to On or Off, depending on whether a user selects or deselects a checkbox.

  function chkStatus(val,bool) {
    if(document.forms[0].hdchkDelete.length){
      document.forms[0].hdchkDelete[val].value= bool ?'on':'off';
    }else{
      document.forms[0].hdchkDelete.value=bool ?'on':'off';
    }
  }

Figure 3 displays a table with checkboxes. A user can select any number of records and delete an operation. The request handling class reads the parameter values for hdchkDelete. It deletes the record if the corresponding checkbox has the value of On.

You can read the selected checkbox index values from the request object String[] values = request.getParameterValues("hdchkDelete");.


Figure 3. Table with checkbox column
Table with checkbox column

To create a table display with radio buttons (see Figure 4) as the column type, you will use these property files:

TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = Select:50:radio:rdInclude
&Employee No:150&First Name:210&Last Name:150
&Dept:150&Job:60

TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
EmployeeSearchResults@com.ibm.optimistic.model.bo.Employee@control
&EmpNo&FirstName&LastName&Dept&Job

The HeaderDisplay property contains the Select:50:radio:rdInclude entry to display the radio buttons.

The user clicks on a radio button to call the following JavaScript function.

  function handleTableRadioCtrl(linkIndex) {
    document.forms[0].iTableLink.value = linkIndex;
  }

For Employee No:111111, handleTableRadioCtrl ('0') calls the above function. The function sets the form variable iTableLink with the selected radio button's index. To get the corresponding record's details, use the iTableLink value. The iTableLink represents the value object's index. (The table data comes from the session; it is stored in the session with the vector of value objects).

You can read the selected radio button index from the request object: String selectedIndex = request.getParameter("iTableLink");.


Figure 4. Table with radio button as column
Table with radio button as column

To create a table display with a textbox/combo box as the column type (see Figure 5), use these property files:

TableHeaderDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = Employee 
No:150&First Name:150:textbox:FirstName&Last Name:150:textbox:LastName
&Dept:150:textbox:Dept&Job:60:textbox:Job&Sex:80:combo:sex

TableValueDisplay.properties (single line of code displayed on multiple lines for viewing convenience):

SearchEmployee.jsp&EmployeeSearchResults = 
EmployeeSearchResults:Sex@com.ibm.optimistic.model.bo.Employee@EmpNo
&control&control&control&control&control

The HeaderDisplay property contains the following entries to display the textbox and combo box:

  • First Name:150:textbox:FirstName for the textbox entry
  • Sex:80:combo:sex for the combo box entry

The TableValueDisplay.properties file contains the session attribute key (Sex) for the combo box after the table data session attribute key (EmployeeSearchResults).

The request processing class reads the parameter values from the request as follows:

FirstNameValues [0], FirstNameValues [1]... to read firstName values
LastNameValues [0], LastNameValues [1]... to read lastName values
String[]FirstNameValues = request.getParameterValues("FirstName");
String[]LastNameValues = request.getParameterValues("LastName");


Figure 5. Table with textbox/combo box as the column
Table with textbox/combobox as column

In conclusion

In this article, you learned how to create a custom tag for table data display with different html controls, including href, checkbox, radio button, text field, and combo box. For complex applications with many JSP pages, you reduce your coding and maintenance efforts with this table tag.



Download

DescriptionNameSizeDownload method
The zip file contains TableTag related fileswa-custmtag_source.zip47 KB HTTP

Information about download methods


Resources

Learn

Discuss

About the author

Photo of Srinivasa Rao Karanam

Srinivasa has more than five years of experience in JavaEE-related technologies. He joined IBM Global Services, India in 2004. Srinivasa currently works on BizPortlets, which are sample portlets developed for the WebSphere Portal Server.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=91791
ArticleTitle=Define a custom tag for table display
publish-date=08232005
author1-email=srkarana@in.ibm.com
author1-email-cc=htc@us.ibm.com

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers