Getting on the Grid with AJAX-Based Tabling - ' A Hands' (
Page 3 of 3 )
-On Example">
You start by providing a Web page that will return the XML data for the Javascript to consume.
My first inclination would have been to write this as a pure servlet (or Struts action, perhaps), but alas the toolkit requires you to have a handle on the JspWriter object, which means it needs to live on a JSP page. This is a serious flaw, in my opinion, but one that Nitobi indicates is being addressed.
ADVERTISEMENT
In any event, rather than plop a bunch of Java code directly on the page, I decided to write a JSP tag to contain the logic. Here's the tag I came up with:
package com.blackbear.ebareview.tags;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import eba.gethandler.GetHandler;
import eba.gethandler.Record;
public class EbaRenderTag extends TagSupport {
private static final long serialVersionUID = -7980664667295184229L;
public int doStartTag() throws JspException {
JspWriter out = this.pageContext.getOut();
HttpServletRequest request =
(HttpServletRequest) this.pageContext.getRequest();
HttpServletResponse response =
(HttpServletResponse) this.pageContext.getResponse();
String startParameter = request.getParameter("start");
if (startParameter == null) {
startParameter = "0";
}
String sortDirection = request.getParameter("SortDirection");
if ((sortDirection == null) || (0 == sortDirection.length())) {
sortDirection = "ASC";
}
Connection con;
try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
con = DriverManager.getConnection
("jdbc:mysql://192.168.1.202/mythconverg" +
"?user=ebademo&password=test");
Statement st = con.createStatement();
String newQuery = "SELECT distinct *FROM program ORDER BY "
+ sortColumn + " " + sortDirection + " limit " +
pageSize + " offset " + ordinalStart + ";";
ResultSet rs = st.executeQuery(newQuery);
GetHandler myGetHandler = new GetHandler(response, out);
myGetHandler.defineField("programid");
for (int i = 0; i < fields.length; i++) {
myGetHandler.defineField(fields[i]);
}
Record curRecord;
String programId;
while (rs.next()) {
programId = rs.getString("programid") +
rs.getString("starttime") + rs.getString("chanid");
curRecord = myGetHandler.createNewRecord(programId);
curRecord.setField("programid", programId);
for (int i = 0; i < fields.length; i++) {
curRecord.setField(fields[i],
rs.getString(fields[i]));
}
myGetHandler.addRecord(curRecord);
}
myGetHandler.writeToClient("UTF-8");
rs.close();
st.close();
con.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return EbaRenderTag.SKIP_BODY;
}
private static String[] fields = { "title", "subtitle",
"chanid", "category", "category_type", "airdate",
"starttime", "endtime" };
}
The first thing that the tag does is to fetch some parameters that the Grid framework needs (the starting row, number of rows, sort column and sort direction). Next, it uses a native MySQL request to fetch the appropriate rows from the database by building a query based on the parameters passed in.
Because the developer is responsible for fetching and processing the data from the persistant data store, the Grid framework allows a lot of freedom as to where the data can come from. On the other hand, it does mean that the developer has to manually code all the fetch and store code himself.
The GetHandler myGetHandler = new GetHandler(response, out); call gets us the Grid object responsible for turning our data into the appropriate XML for the AJAX client. We set up the fields that we'll be returning using the defineField method, and then supply the data using the createNewRecord, setField, and addRecord methods. Finally, the writeToClient method sends the data back to the client.
The JSP page that uses this tag is simplicity itself:
<% taglib uri = "http://www.blackbear.com/ebatags" prefix = "eba" %>
<eba:ebarender/>
On the client side, the JSP page loads the Javascript and style sheets needed for the Grid client, and then uses the eba:grid tag to request a table be placed in the document:
Note that the onLoad clause in the body tag makes sure that the grid is initialized correctly when the page first loads. The gethandler parameter of the grid tag tells the tag what page to request to get the data (in this case, the JSP page with the custom tag we just wrote). The pagingmode defines how the page scrolls, and can either be none (the page doesn't scroll at all), Standard (the page scrolls to the limit of the data returned by the fetch call) or LiveScrolling (the page scrolls by making calls to the gethandler as needed to move up or down in the dataset. The tag functions well without explicitly defining the columns, but if you want special formatting or more readable labels, you should define the columns.
For some reason, if datasourcesizeestimate isn't set to a fairly high number, the tag fails to correctly fetch all the data. This quirk led me down a wild goose chase of trial-and-error debugging for an hour or so, and was only finally resolved by a call to the company.
Here's an example of what the finished product looks like:
A few comments about the usability of the table itself. Page up and page down work, but only if you already have your cursor focus inside the table. Also, after a few page-ups or downs, it seems to lose focus and you have to mouse a cell inside the table to get it working again. Up and down arrow can also scroll through the rows. You can also copy and paste to and from Excel. Clicking on column titles resorts by that column.
By implementing another handler, you can also made modifications to data using the table and save them back to the database. It is also possible to implement Javascript handlers for events such as mouse click and data modification, and a full Javascript library is provided to allow access to the data.
One feature I would have liked to have seen is the ability to use back and forward buttons to scroll through the data a page at a time. This is a common design seen with tabled web data. I'm sure that it could be easily be implemented using the Grid toolkit and some custom Javascript, but I would have prefered it to be available out of the box.
In summary, the EBA Grid AJAX toolkit offers an easy to code but powerful way to put dynamic scrolling tables on your web site. However, the documentation needs some work, there are some painful quirks in the implementation, and at $399 per developer, it may be a bit pricey for the amount of functionality the software delivers.