10 votes) ( |
Downloadsourcecode - 242 KB
While you are implementing tables in Java web applications, probably you are adding several additional features to the table. Pagination is a quite common requirement that is added to the tables with larger number of data; ordering table data by the column is also a frequent requirement. There are a lot of other requirements that are occasionally added to the tables such as filtering by keyword, changing the number of rows that will be shown per page, etc. There are lot of plug-ins that enable all these functionalities - one of them is the jQuery DataTables plug-in that is explained in this article.
The jQuery DataTables plug-in is an excellent client-side component that can be used to create rich-functional tables in a web browser. This plug-in adds a lot of functionalities to plain HTML tables that are placed in web pages, such as filtering, paging, sorting, changing page length, etc. Although, by default it is pure client side, it might be configured to use data from the server via AJAX calls in order to improve performance. However, to integrateDataTables with server-side code, the developer must know the protocols and parameters that are sent by DataTables and how to use them on the server side.
This article shows how the jQuery DataTables plug-in can be integrated into Java web applications. It contains step by step examples that show how theDataTables plug-in interacts with server-side components. There are a lot of server-side frameworks in Java such as standardservlets, JSP, JSF, Spring, Struts, etc., and this article will not cover them all. In this article, I will focus on plainservlets to demonstrate how you can create high performance AJAX-based data tables with standard Javaservlets.
The goal of this article is to show how you can implement fully functional, high performance tables using thejQuery DataTables plug-in. This is a JavaScript plug-in implemented using thejQuery library that handles all the necessary interaction with the user on the client-side.
jQuery DataTables is a powerful JavaScript plug-in that automatically adds several functionalities such as dropdown for selecting how much items should be shown per page, text box for filtering results, sorting functionality attached to table headers, and pagination on the bottom of the table. No server-sidecode is required for this as all the functionality is implemented as a set of JavaScript handlers that use data found in HTML. The following figure shows how a plain HTML table looks after applying the DataTables plug-in:
The DataTables plug-in adds a "Show XXX entries" dropdown above the table, enabling the user to choose whether he wants to see 10, 25, 50, or 100 records per page and a search text box that enables the user to filter by keyword records that should be shown in the table. This plug-in also adds sorting functionality that enables the user to sort results by clicking on the column header. Below the table, there is pagination that enables the user to navigate through the pages and text that automatically displays which records are currently displayed. All these functionalities are added by default, and all you need is a single line ofcode:
$('table#myTable').dataTable();
In this example, an HTML table with ID "myTable
" is found in the DOM of the web page in the browser and the DataTables plug-in is applied in order to add the widgets shown in the previous figure.
In the default mode, there is minimal code required in the Java web application - the web server can generate a plain HTML table in standard format. The client-side JavaScript component will use whatever gets generated and add client-side functionalities. In this client-side mode, DataTables takes all the table rows from the<tbody></tbody>
section and performs filtering, paging, and sorting directly on these elements as in in-memory objects. This is the fastest way to use DataTables, but it requires that the server returns all data in a single call, loads all these rows as in-memory JavaScript objects, and render them dynamically in DOM. This might cause performance issues with server call and memory usage on the client. However, this minimizes the number of requests sent to the server because once the table is loaded, the server is not used at all.
On the other hand, it is possible to implement client-server interaction by configuring DataTables to query the server via AJAX calls in order to fetch the required data. In this case, the plug-in will call the server side page, post information about the required data, take the response from the server, and refresh the table. On the DataTables site, there is an example of server-side configuration where thejQuery DataTables plug-in sends requests to a PHP page and gets data that should be shown in the current view. The server response is formatted as aJSON object, parsed on the client side, and displayed in the table body. The following figure shows a trace of the calls sent to the server (captured using the Firebug add-in for Firefox):
In this case, each event (changing the number of items that should be displayed per page, entering keyword in search filter, sorting, pressing pagination button, etc.) triggers the DataTables plug-in to send information about the current page, the search filter, and the sort column to the server page. As shown in the request, the server page returnsJSON as a result and DataTables uses that data array when displaying the current table page. In this mode, instead of taking the complete page at once, several smaller requests are sent whenever new information is required, and minimal amount of data is returned from the server. DataTables, in this example, calls the/CompanyAjaxDataSource URL and sends information about the user action. A full example of the server-side configuration of thejQuery DataTable plug-in can be found here. A major problem with the server-side mode is the implementation of server-side logic that accepts parameters from the client-side component, performs action, and returns data as expected.
This article explains how to configure jQuery DataTables and implement server-side logic with a Javaservlet both in client-side and server-side mode. In your application, you can choose which of these two modes suites you best.
This sample shows how you can generate a table of companies and add the jQuery DataTables plug-in to the HTML table. The code is logically organized in the MVC structure:
There is also one utility package containing classes transforming Java objects intoJSON (this is required because the jQuery DataTables plug-in communicates with server-side code via JSON objects).
The Model is a Company
class that contains the following properties:
In this example, a database or any other datasource is not used, so I have used a class calledDataRepository
that contains a hardcoded list of companies. This class returns the companies that will be shown, using the following call:
DataRepository.GetCompanies();
In the following sections are presented two major cases of usage of DataTables in Java web applications.
As mentioned above, the jQuery DataTables plug-in can be applied on a static HTML structure in the browser. In that case, you will need to generate HTMLcode for the table on the server-side. In this example, I have generated a table structure using a JSP page shown in the listing below:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="jquery.datatables.model.*"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title>Applying jQuery DataTables plugin in the Java Server application</title> <link href="media/dataTables/demo_page.css" rel="stylesheet" type="text/css" /> <link href="media/dataTables/demo_table.css" rel="stylesheet" type="text/css" /> <link href="media/dataTables/demo_table_jui.css" rel="stylesheet" type="text/css" /> <link href="media/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" media="all" /> <link href="media/themes/smoothness/jquery-ui-1.7.2.custom.css" rel="stylesheet" type="text/css" media="all" /> <script src="scripts/jquery-1.4.4.min.js" type="text/javascript"></script> <script src="scripts/jquery.dataTables.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $("#companies").dataTable({ "sPaginationType": "full_numbers", "bJQueryUI": true }); }); </script> </head> <body id="dt_example"> <div id="container"> <div id="demo_jui"> <table id="companies" class="display"> <thead> <tr> <th>Company name</th> <th>Address</th> <th>Town</th> </tr> </thead> <tbody> <% for(Company c: DataRepository.GetCompanies()){ %> <tr> <td><%=c.getName()%></td> <td><%=c.getAddress()%></td> <td><%=c.getTown()%></td> </tr> <% } %> </tbody> </table> </div> </div> </body> </html>
In this JSP page is included all the necessary JavaScript libraries, and HTML code for the company table is generated. For demonstration purposes, a simple loop generates aTR
for each company that is returned by the repository class. However, in your applications, you can use any server-side processing you want (JavaBeans, JSTL components, etc.) because thejQuery DataTables plug-in is independent of the applied server-side technology.
In the document-ready JavaScript event handler, the plain generated table is enhanced with thejQuery DataTables plug-in. There are two parameters that are passed to the plug-in initialization function (these are two parameters I always use):
sPagination
- instructing the plug-in to generate pagination with numbers instead of two previous-next buttons, as is generated by default.bJQueryUI
- applying standardjQueryUI styles.Instead of the plain HTML table, the following component is shown on the client side:
All actions you see in the component are implemented on the client-side (e.g., when you enter text in the text box,TR
elements are filtered). This is the fastest way for the user under the assumption that the time required to load the table is not too big. If you have a huge amount of data, you might consider using DataTables in AJAX mode where only partial results are returned to the plug-in.
In AJAX mode, only a minimal amount of data is provided to the plug-in using JSON. The plug-in sends AJAX requests to the server containing information of the current state of the view (current page, filter criterion, page number, etc). The server accepts the AJAX call and determines what information should be shown on the client side and returns a JSON response back to the plug-in. Note that in this case, processing must be implemented on the server side.
In AJAX mode, no relevant information is generated in the view page, therefore it can be even a static HTML page. An example of the HTML page used in this example is shown in the following listing:
<!DOCTYPE html> <html> <head> <title>Using jQuery DataTables plugin with AJAX source implemented in Java web application</title> <link href="media/dataTables/demo_page.css" rel="stylesheet" type="text/css" /> <link href="media/dataTables/demo_table.css" rel="stylesheet" type="text/css" /> <link href="media/dataTables/demo_table_jui.css" rel="stylesheet" type="text/css" /> <link href="media/themes/base/jquery-ui.css" rel="stylesheet" type="text/css" media="all" /> <link href="media/themes/smoothness/jquery-ui-1.7.2.custom.css" rel="stylesheet" type="text/css" media="all" /> <script src="scripts/jquery-1.4.4.min.js" type="text/javascript"></script> <script src="scripts/jquery.dataTables.min.js" type="text/javascript"></script> <script type="text/javascript"> $(document).ready(function () { $("#companies").dataTable({ "bServerSide": true, "sAjaxSource": "/JQueryDataTables/CompanyAjaxDataSource", "bProcessing": true, "sPaginationType": "full_numbers", "bJQueryUI": true }); }); </script> </head> <body id="dt_example"> <div id="container"> <div id="demo_jui"> <table id="companies" class="display"> <thead> <tr> <th>Company name</th> <th>Address</th> <th>Town</th> </tr> </thead> <tbody> </tbody> </table> </div> </div> </body> </html>
As you can see, the table body is completely empty because the jQuery DataTables plug-in will be populated using the AJAX call. In the document ready event, DataTables is initialized with three additional parameters:
bServerSide
that instructs DataTables plug-in to take information from the server-sidesAjaxSource
that defines that URL that should be called by the plug-in to take the databProcessing
(optional) used to show the "Processing" message while the AJAX call is executingThe key part of the server-side code is the component that will provide data to thejQuery DataTables plugin - in this case, this is the component on the server-side that reacts when thesAjaxSource
page is called. This can be anything:servlet, JSP page, or any server-side code that returns a JSON response based on the parameters sent from the plug-in. ThejQuery DataTables plug-in sends a lot of parameters to the server-side; you can find a detailed documentation on theDataTables site, but here are the most important ones:
sEcho
- Integer value that is used by DataTables for synchronization purpose. Response from the server-sidecode should return the same value to the plug-in.sKeyword
- Text entered in the filter text box and it should be used for filtering records.iDisplayStart
- Dirst record that should be shown (used for pagination).iDisplayLength
- The number of records that should be returned (this value is equal to the value selected in the 'ShowXXX items per page' dropdown). This value is also used for pagination.iColumns
- The number of columns in the table.iSortingCols
- The number of columns used for ordering. Usually this value is 1, but if the user orders results by more than one column (holding the SHIFT key while clicking on the header cells), the DataTables plug-in will send information about the number of columns you should use for ordering results.iSortCol_0
, iSortCol_1
, iSortCol_2
, etc. - The IDs of the columns used for ordering results. If results are ordered by one column, you should order results by theiSortCol_0
column.sSortDir_0
, sSortDir_1
, sSortDir_2
, etc. - The sort direction for each of the columns used for ordering. If results are ordered by one column, an "asc" or "desc" value will be returned in thesSortDir_0
parameter. In the case of multi-column ordering, each parameter in this array will have a direction that matches the column in theiSortCol_
parameter.The server-side component should have the GET method handler that will be called when the plug-in sends an AJAX request. This method should take the parameters described above, process data, and prepare values that will be sent to the plug-in. An example of the response that is returned back from the servlet is shown in the example below:
{ "sEcho":"1", "iTotalRecords":97, "iTotalDisplayRecords":3, "aaData":[ ["1","a1","a2","a3"], ["2","b1","b2","b3"], ["3","c1","c2","c3"] ] }
Values that the server returns to the DataTables plug-in are:
sEcho
- An integer value that is used by DataTables for synchronization purposes. On each call sent to the server-side page, the DataTables plug-in sends the sequence number in thesEcho
parameter. The same value has to be returned in the response because DataTables uses this for synchronization and matching requests and responses.iTotalRecords
- The integer value that represents the total unfiltered number of records that exist on the server-side and that might be displayed if no filter is applied. This value is used only for display purposes; when the user types in a keyword in the search text box, DataTables shows a "Showing 1 to 10 of 23 entries (filtered from 51 total entries)" message. In this case, theiTotalRecords
value returned in the response equals 51.iTotalDisplayedRecords
- The integer value that represents the number of records that match the current filter. If the user does not enter any value in the search text box, this value should be the same as theiTotalRecords
value. The DataTables plug-in uses this value to determine how many pages will be required to generate pagination - if this value is less or equal than the current page size, the pagination buttons will be disabled. When the user types in A keyword in the search text box, DataTables shows a "Showing 1 to 10 of 23 entries (filtered from 51 total entries)" message. In this case, theiTotalDisplayedRecords
value returned in the response equals 23.aaData
- A two-dimensional array that represents the cell values that will be shown in the table. When DataTables receives data, it will populate the table cells with values from theaaData
array. The number of columns in the two dimensional array must match the number of columns in the HTML table (even the hidden ones), and the number of rows should be equal to the number of items that can be shown on the current page (e.g., 10, 25, 50, or 100 - this value is selected in the "Show XXX entries" dropdown).Any Java server-side component that accepts the parameters described above and returns the expectedJSON result can be used. In this example, a plainservlet is used for demonstration purposes, but you can use any other component. Theservlet used in this example has a doGet
method that accepts parameters and returns aJSON object. The following listing shows an example of the get method:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { JQueryDataTableParamModel param = DataTablesParamUtility.getParam(request); String sEcho = param.sEcho; int iTotalRecords; // total number of records (unfiltered) int iTotalDisplayRecords;//value will be set when code filters companies by keyword JSONArray data = new JSONArray(); //data that will be shown in the table //Filtering companies //Ordering companies //Pagination //Generate JSON response }
The servlet is mapped on the /CompanyAjaxDataSource URL and the get method takes parameters sent by the plug-in. For this purpose, we have added two utility classes:JQueryDataTableParamModel
containing the properties that are sent by the plug-in, andDataTablesParamUtility
that loads the values of the JQueryDataTableParamModel
from the request. Also, in this part ofcode, values that will be sent to the plug-in are prepared. The actualcode that populates properties from the request is omitted, but you can find thecode in the code example. Note that properties in this class have the same names as the parameters sent by the plug-in so you can easily follow thecode.
The first action that needs to be done on the server-side is filtering of companies by the keyword provided by the plug-in. Thiscode is shown in the listing below:
iTotalRecords = DataRepository.GetCompanies().size(); List<Company> companies = new LinkedList<Company>(); for(Company c : DataRepository.GetCompanies()){ if( c.getName().toLowerCase().contains(param.sSearch.toLowerCase()) || c.getAddress().toLowerCase().contains(param.sSearch.toLowerCase()) || c.getTown().toLowerCase().contains(param.sSearch.toLowerCase())) { companies.add(c); // Add a company that matches search criterion } } iTotalDisplayRecords = companies.size(); //Number of companies that matches search criterion should be returned
The keyword entered in the filter text box is placed in the param.sSearch
string. In this example,iTotalRecords
is set to the total number of companies that are in the list, a search is performed by the company properties, and a new list of filtered companies that contains the keyword are added in the list. The number of filtered companies is placed in the iTotalDisplayRecords
variable. In the second part of thecode, companies are ordered by the column. The followingcode shows ordering done using the index of the column that should be sorted and the direction. On the server-sidecode, you will need to know what column indexes match properties in theCompany
class.
final int sortColumnIndex = param.iSortColumnIndex; final int sortDirection = param.sSortDirection.equals("asc") ? -1 : 1; Collections.sort(companies, new Comparator<Company>(){ @Override public int compare(Company c1, Company c2) { switch(sortColumnIndex){ case 0: return c1.getName().compareTo(c2.getName()) * sortDirection; case 1: return c1.getAddress().compareTo(c2.getAddress()) * sortDirection; case 2: return c1.getTown().compareTo(c2.getTown()) * sortDirection; } return 0; } });
This is a simplified case where it is assumed that ordering is done by one single column and that index of the column that will be used for sorting and the direction are placed in theparam.iSortColumIndex
and param.sSortDirection
properties.
The last functionality that should be implement is pagination. The current page and the length are placed in theiDisplayStart
and iDisplayLength
properties, and these properties are used to get the sub list that should be returned to the plug-in. An example is shown in the following listing:
if(companies.size()< param.iDisplayStart + param.iDisplayLength) companies = companies.subList(param.iDisplayStart, companies.size()); else companies = companies.subList(param.iDisplayStart, param.iDisplayStart + param.iDisplayLength);
When the list is prepared, a JSON object that represents the response should be created. Thecode for creating the JSON response is:
try { JSONObject jsonResponse = new JSONObject(); jsonResponse.put("sEcho", sEcho); jsonResponse.put("iTotalRecords", iTotalRecords); jsonResponse.put("iTotalDisplayRecords", iTotalDisplayRecords); for(Company c : companies){ JSONArray row = new JSONArray(); row.put(c.getName()).put(c.getAddress()).put(c.getTown()); data.put(row); } jsonResponse.put("aaData", data); response.setContentType("application/json"); response.getWriter().print(jsonResponse.toString()); } catch (JSONException e) { // TODO Auto-generated catch block e.printStackTrace(); response.setContentType("text/html"); response.getWriter().print(e.getMessage()); }
This example has the echo, total records, and total filtered records properties. Also, the data properties have the matrix containing the name, address, and town information. When this object is returned to the plug-in, filtered information is shown in the table on the client side.
In this example, processing is done in the plain source code; however, in your application, you can use other methods such as SQL, Stored Procedures, HQL, etc. The only important thing is that you returnJSON response with objects that match the current state on the table.
This example shows how you can create effective, fully functional tables in a Java web application using thejQuery DataTables plug-in. Using the code examples in the article, you can significantly enhance the look and functionalities of your web application. I recommend that you try it - when you integratejQuery DataTables in a few tables in your application, you will see that the implementation is straightforward and that you will be, with some practice, able to implement a lot of functionalities with very little effort.
You can download the code example in the attached project that was created in the Eclipse Java EE IDE for Web Developers, that runs on a Tomcat 7.0 web server. If you have the same configuration, you can run and try this project, and if you have any other development environment, I suggest you create a new project, configure it, and add classes that you can find in the project. I hope that this example will help you to create better table interfaces.
If you are interested in adding full Web 2.0 CRUD functionalities to the JQuery DataTable, I recommend another article Adding data management (CRUD) functionalities to web tables, where I have exlplained how you can configure plugin to automaticaly add inline editing, deleting, and adding new records functionalities to theJQuery DataTables plugin.
This article, along with any associated sourcecode and files, is licensed under The Code Project Open License (CPOL)