Wednesday 16 February 2011

JQ AJAX server processing with PAGING

This is the continuation of the previous tutorial on serverSide JQuery DataTable with JSP tutorial. After you finish this tutorial you may have noticed that the paging buttons(two_button) did not work. If studied one can see that the original server_processing.php example had the same discrepancy.


I believe Allan JARDINE has done a tremendous contribution and deserves a decent return anyway. The mentioned example is basicly an AJAX server processing example and it is not a paging example. So, it may not be adequate to name it as a discrepancy. Nevertheless, it has to be fixed to continue our endevour.


Thee is a simple solution to this which requires a better understanding of server processing with a 'json' object and event handling.

Define a new(global) json variable above the $(document).ready function:
...
json jsonARS;
...


Get the json variable returned from the server and put it in the global jsonARS var.

Place in the $(document).ready function:
...
"sAjaxSource": "./server_processing.jsp",
"fnServerData": function ( sSource, aoData, fnCallback ) {
/* Add some extra data to the sender */
aoData.push( { "name": "more_data", "value": "my_value" } );
$.getJSON( sSource, aoData, function (json) {
/* Do whatever additional processing you want on the callback, then tell DataTables */
//alert("OK");
jsonARS = json;
fnCallback(json);
} );
}
...


Handle the two_button bttons with:
...
$('#example_prev').click( function() {
...

get the osettings structure pointer to acces the _iDisplayLength

...
var oSettings = oTable.fnSettings();
...


use jsonARS to access the jsonARS.iTotalDisplayRecords

...

Please note that, I handle the below cases in event processing:
1- Do not go forward over the end of the total data.
2- Do not go backwards before the beginning of the total data.
3- Do not go further than the end of the displayed(selected) data.
etc.

The complete working code follows.

Cheers.

Ali R+




serverSideJQdtARS_3.jsp
-----------------------
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="shortcut icon" type="image/ico" href="DataTables-1.7.5/media/images/favicon.ico" />

<title>DataTables example</title>
<style type="text/css" title="currentStyle">
@import "DataTables-1.7.5/media/css/demo_page.css";
@import "DataTables-1.7.5/media/css/demo_table.css";
</style>
<script type="text/javascript" language="javascript" src="DataTables-1.7.5/media/js/jquery.js"></script>
<script type="text/javascript" language="javascript" src="DataTables-1.7.5/media/js/jquery.dataTables.js"></script>
<script type="text/javascript" charset="utf-8">
var jsonARS;
$(document).ready(function() {
oTable = $('#example').dataTable( {
"bProcessing": true,
"bServerSide": true,
// "sPaginationType": "full_numbers",
"sPaginationType": "two_button",
"aaSorting": [[0, 'desc'], [1, 'desc']],

"sAjaxSource": "./server_processing.jsp",
"fnServerData": function ( sSource, aoData, fnCallback ) {
/* Add some extra data to the sender */
aoData.push( { "name": "more_data", "value": "my_value" } );
$.getJSON( sSource, aoData, function (json) {
/* Do whatever additional processing you want on the callback, then tell DataTables */
//alert("OK");
jsonARS = json;
fnCallback(json);
} );
},
"fnCallback": function ( json ) {

}
} );
$('#example_next').click( function() {
//alert('paginate click handler');
var oSettings = oTable.fnSettings();
// alert("iDisplayStart="+oSettings._iDisplayStart+
// "iDisplayLength="+oSettings._iDisplayLength+
// "iTotalRecords="+jsonARS.iTotalRecords+
// "iTotalDisplayRecords="+jsonARS.iTotalDisplayRecords
// );

if (((oSettings._iDisplayStart+ oSettings._iDisplayLength) < jsonARS.iTotalRecords) &&
(jsonARS.iTotalDisplayRecords == oSettings._iDisplayLength))
oSettings._iDisplayStart += oSettings._iDisplayLength;
oSettings.oApi._fnDraw( oSettings );
} );
$('#example_prev').click( function() {
//alert('paginate click handler');
var oSettings = oTable.fnSettings();
oSettings._iDisplayStart -= oSettings._iDisplayLength;
if (oSettings._iDisplayStart < 0) oSettings._iDisplayStart = 0;
oSettings.oApi._fnDraw( oSettings );
} );
} );


</script>
</head>
<body id="dt_example">
<div id="container">
<div class="full_width big">
<i>DataTables</i> server-side processing example
</div>
<H1>Live example</H1>
<DIV id=dynamic>
<TABLE id=example class=display border=0 cellSpacing=0 cellPadding=0>
<THEAD>
<TR>
<TH width="20%">Rendering engine</TH>
<TH width="25%">Browser</TH>
<TH width="25%">Platform(s)</TH>
<TH width="15%">Engine version</TH>
<TH width="15%">CSS grade</TH></TR></THEAD>
<TBODY>
<TR>
<TD class=dataTables_empty colSpan=5>Loading data from server
</TD>
</TR>
</TBODY>
</TABLE>
</DIV>
</div>
</body>
</html>

server_processing.jsp
---------------------

<%--
Document : server_processingARS.jsp
Created on : 04.?ub.2011, 17:53:53
Author : Ali Riza SARAL
--%>

<%@page import="java.sql.Connection"%>
<%@page import="atg.taglib.json.util.*"%>
<%@page import= "java.util.*"%>
<%@page import= "java.sql.*"%>
<%@page import= "org.apache.commons.lang.*"%>


<%
class Utilities {

Utilities() {
}

;

String escape_string(String input) {
return StringEscapeUtils.escapeHtml(input);
}
}
%>

<%
System.out.println("test arsssssssssssssssssssssss");
Utilities util = new Utilities();
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Easy set variables
*/

/* Array of database columns which should be read and sent back to DataTables. Use a space where
* you want to insert a non-database field (for example a counter or static image)
*/
String[] aColumns = {"engine", "browser", "platform", "version", "grade"};

/* Indexed column (used for fast and accurate table cardinality) */
String sIndexColumn = "id";

/* DB table to use */
String sTable = "ajax";
/* Database connection information */
String user = "root";
String password = "3391309";
String db = "jquerydb";
String server = "localhost";

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* If you just want to use the basic configuration for DataTables with PHP server-side, there is
* no need to edit below this line
*/

/*
* MySQL connection
*/

Connection conn = null;

try {
Class.forName("com.mysql.jdbc.Driver").newInstance();
conn = DriverManager.getConnection("jdbc:mysql://localhost/" + db,
user, password);

if (!conn.isClosed()) {
System.out.println("Successfully connected to "
+ "MySQL server using TCP/IP...");
}

} catch (Exception e) {
System.err.println("Exception: " + e.getMessage());
} finally {
// try {
// if (conn != null) {
// //conn.close();
// }
// } catch (SQLException e) {
// }
}

/*
* Paging
*/
String sLimit = "";
if (request.getParameter("iDisplayStart") != null
&& request.getParameter("iDisplayLength") != "-1") {
sLimit = "LIMIT " + util.escape_string(request.getParameter("iDisplayStart")) +
", " + util.escape_string(request.getParameter("iDisplayLength"));
}

/*
* Ordering
*/
String sOrder = "";

if (request.getParameter("iSortCol_0") != null) {
sOrder = "ORDER BY ";

for (int i = 0; i < Integer.valueOf(request.getParameter("iSortingCols")); i++) {
if (request.getParameter("bSortable_" + Integer.valueOf(request.getParameter("iSortCol_" + String.valueOf(i)))).equals("true")) {
sOrder += aColumns[Integer.valueOf(request.getParameter("iSortCol_" + String.valueOf(i)))] + " "
+ util.escape_string(request.getParameter("sSortDir_" + String.valueOf(i))) + ", ";
}
}

sOrder = sOrder.substring(0, sOrder.length()-2);
if (sOrder.equals("ORDER BY")) {
sOrder = "";
}
sOrder += " ";
}



/*
* Filtering
* NOTE this does not match the built-in DataTables filtering which does it
* word by word on any field. It's possible to do here, but concerned about efficiency
* on very large tables, and MySQL's regex functionality is very limited
*/
String sWhere = "";
System.out.println("sSearch="+ request.getParameter("sSearch"));

if (request.getParameter("sSearch") != "") {
sWhere = "WHERE (";
for (int i = 0; i < aColumns.length; i++) {
sWhere += aColumns[i] + " LIKE '%" + util.escape_string(request.getParameter("sSearch")) + "%' OR ";
}
sWhere = sWhere.substring(0, sWhere.length()-3);
sWhere += ')';
}

/* Individual column filtering */

for (int i = 0; i < aColumns.length; i++) {
if (request.getParameter("bSearchable_" + String.valueOf(i)) == "true"
&& request.getParameter("sSearch_" + String.valueOf(i)) != "") {
if (sWhere == "") {
sWhere = "WHERE ";
} else {
sWhere += " AND ";
}
sWhere += aColumns[i] + " LIKE '%" + util.escape_string(request.getParameter("sSearch_" + String.valueOf(i))) + "%' ";
}
}

/*
* SQL queries
* Get data to display
*/
String strColumnNames = "";

for (int i=0;i < aColumns.length;i++){
strColumnNames = strColumnNames + ", "+ aColumns[i];
}
strColumnNames = strColumnNames.substring(1);

String sQuery = "SELECT "
+ strColumnNames
+ " FROM " + sTable
+ " "
+ sWhere
+ sOrder
+ sLimit;

System.out.println("sQuery="+sQuery);


Statement s = conn.createStatement();
s.executeQuery(sQuery);

/* Data set length after filtering */
ResultSet rs = s.getResultSet();
int count = 0;
while (rs.next()) {
String engineCol = rs.getString("engine");
System.out.println("engine = " + engineCol);
++count;
}
int iFilteredTotal = count;
rs.close();
s.close();
System.out.println("Selected count="+iFilteredTotal);

/* Total data set length */
s = conn.createStatement();
String sQuery2 = " SELECT COUNT(*) AS rowcount FROM " + sTable;
rs = s.executeQuery(sQuery2);
rs.next();
int iTotal = rs.getInt("rowcount");
rs.close();
s.close();
System.out.println("Total count="+iTotal);

/*
* Output
*/
String output = "{" +
"\"sEcho\" : "+ request.getParameter("sEcho")+ ", " +
"\"iTotalRecords\" : " + String.valueOf(iTotal) + ", " +
"\"iTotalDisplayRecords\" : " + String.valueOf(iFilteredTotal) + ", " +
// "\"iDisplayLength\" : " + "10" + ", " +
// "\"iDisplayStart\" : " + "10" + ", " +
"\"aaData\" : [" + "";

Object[][] aaData= new Object[iFilteredTotal][5];

s = conn.createStatement();
s.executeQuery(sQuery);
rs = s.getResultSet();

int rowNum=0;
while ( rs.next() )
{
System.out.println("rowNum="+ rowNum);
//if (rowNum > 3) break;
Object[] aRow = {"","","",0,""};
Object[] row = {"","","",0,""};

for ( int i=0 ; i<aColumns.length ; i++ )
{
System.out.println("i="+ i +" colVal="+ rs.getString( aColumns[i]));
if ( aColumns[i] == "version" )
{
/* Special output formatting for 'version' column */
row[i] = (rs.getInt( aColumns[i] )==0) ? "-" : rs.getInt(aColumns[i]);
}
else if ( aColumns[i] != " " )
{
/* General output */
row[i] = rs.getString( aColumns[i]);
}
}
aaData[rowNum] = row;
System.out.println("rowNum("+rowNum +") row[0]="+row[0].toString());
rowNum++;
}
rs.close();
s.close();

String valRow = " [ ";
for (int rowN = 0; rowN < iFilteredTotal; rowN++) {
System.out.println("rowN="+rowN);
for (int colN = 0; colN < aColumns.length; colN++) {
System.out.println(" colN="+colN);
if (aColumns[colN] == "version" )
valRow = valRow + " " + aaData[rowN][colN].toString() + " , ";
else if ( aColumns[colN] != " " )
valRow = valRow + "\"" + aaData[rowN][colN].toString() + "\" , ";
}
valRow = valRow.substring(0, valRow.length()-2);
valRow = valRow + " ], ";
System.out.println("valRow="+valRow);
output = output + valRow;
valRow = " [ ";
}
System.out.println("output="+output);
output = output.substring(0, output.length()-2);
output = output + " ] }";

out.print(output);
%>