Review
In the previous section, we created the core Event management system. In this section, we will work on the View layer. Data will be presented in a table. We will use DataTables, a jQuery plugin, and a custom JavaScript function to provide advance features to our table like sorting and searching.Where am I?
Table of Contents
DataTables
What is DataTables?DataTables is a plug-in for the jQuery Javascript library. It is a highly flexible tool, based upon the foundations of progressive enhancement, which will add advanced interaction controls to any HTML table. (Source: http://datatables.net/)
Before we proceed with the development, let's preview the final output:
Development
Since we've already discussed the controller and service classes in the previous section, we just need to discuss the JSP pages. We actually have five JSP pages:- Primary page
- Supporting pages
- add-dialog.jsp
- edit-dialog.jsp
- delete-dialog.jsp
- generic-dialog.jsp
1. Primary page
The primary page contains the main view. It's a single page containing the following sections:- URLs
- Imports
- Menu
- Table
- Dialogs
- Conversion of links to buttons
- Attaching link functions
- Retrieval of records and conversion to DataTables
event-page.jsp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %> | |
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %> | |
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %> | |
<c:url value="/" var="rootUrl"/> | |
<c:url value="/resources" var="resourcesUrl"/> | |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<!-- CSS Imports--> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/jquery/dark-hive/jquery-ui-1.8.6.custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/datatables/custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/main/main.css"/> | |
<!-- JS Imports --> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-1.5.2.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-ui-1.8.12.custom.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/datejs/date.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/datatables/jquery.dataTables.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/util/util.js"></script> | |
<title>Events</title> | |
</head> | |
<body class="ui-widget-content"> | |
<div id="menu"> | |
<ul> | |
<li><a href="${rootUrl}event">Events (DataTables)</a></li> | |
<li><a href="${rootUrl}jqgrid/event">Events (jQgrid)</a></li> | |
<li><a href="${rootUrl}error">Errors</a></li> | |
<li><a href="${rootUrl}monitor/event">Monitor Events</a></li> | |
<li><a href="${rootUrl}monitor/error">Monitor Errors</a></li> | |
</ul> | |
<br style="clear:left"/> | |
</div> | |
<h3 class="title">Events - DataTables Version</h3> | |
<table id='eventTable'> | |
<thead> | |
<tr> | |
<th></th> | |
<th>Id</th> | |
<th>Name</th> | |
<th>Description</th> | |
<th>Participants</th> | |
<th>Date</th> | |
</tr> | |
</thead> | |
<tbody> | |
</tbody> | |
</table> | |
<c:if test="${empty events}">There are currently no events in the list.</c:if> | |
<div class="control"> | |
<span><a href="#" id="addLink">Add</a></span> | |
<span><a href="#" id="editLink">Edit</a></span> | |
<span><a href="#" id="deleteLink">Delete</a></span> | |
</div> | |
<jsp:include page="/WEB-INF/jsp/dialogs/addDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/editDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/deleteDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/genericDialog.jsp"/> | |
<script type="text/javascript"> | |
$(function() { | |
// Convert links to buttons | |
$('#addLink, #editLink, #deleteLink').button(); | |
// Assign a function to addLink | |
// Displays a dialog form for adding a new record | |
$("#addLink").click(function() { | |
// Show the dialog | |
$( "#addDialog" ).dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
return false; | |
}); | |
// Assign a function to editLink | |
// Checks first if a record is selected from the table | |
// Then it retrieves that record via jQuery's data()storage method | |
// Finally it displays a dialog form for editing the selected record | |
$("#editLink").click(function() { | |
var tId = $('input:radio[name=eventRadio]:checked').val(); | |
if (tId == null) { | |
$("#genericDialog").text("Select a record first!"); | |
$("#genericDialog").dialog( | |
{ title: 'Error', | |
modal: true, | |
buttons: {"Ok": function() { | |
$(this).dialog("close");} | |
} | |
}); | |
} else { | |
// Retrieve record | |
var record = null; | |
for (var i=0; i<$('#eventTable').data('records').length; i++) { | |
if ($('#eventTable').data('records')[i].id == tId) { | |
record = $('#eventTable').data('records')[i]; | |
break; | |
} | |
} | |
// Assign record to form fields | |
$('#editForm #ename').val(record.name.toString()); | |
$('#editForm #edate').val(new Date(record.date).toString('yyyy-MM-dd')); | |
$('#editForm #edescription').val(record.description.toString()); | |
$('#editForm #eparticipants').val(record.participants.toString()); | |
// Show the dialog | |
$("#editDialog").dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
} | |
return false; | |
}); | |
// Assign a function to deleteLink | |
// Checks first if a record is selected from the table | |
// Finally it displays a dialog form for deleting the selected record | |
$("#deleteLink").click(function() { | |
// show dialog box | |
var tId = $('input:radio[name=eventRadio]:checked').val(); | |
if (tId == null) { | |
$("#genericDialog").text("Select a record first!"); | |
$("#genericDialog").dialog( | |
{ title: 'Error', | |
modal: true, | |
buttons: {"Ok": function() { | |
$(this).dialog("close");} | |
} | |
}); | |
} else { | |
$("#deleteDialog").dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
} | |
return false; | |
}); | |
// Retrieve all records for the first time | |
// Converts table to DataTable as well | |
$.getRecords('#eventTable', '${rootUrl}event/getall', | |
['id', 'name', 'description', 'participants', 'date'], | |
function() { | |
$('#eventTable').dataTable( { | |
"bJQueryUI": true, | |
"sPaginationType": "full_numbers" | |
}); | |
}); | |
}); | |
</script> | |
</body> | |
</html> |
Let's discuss each section:
a. URLs
The following section declares two URLs which uses the JSTL core tag: the root and the resources URLs respectively. These URLs are here for reusability purposes:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<c:url value="/" var="rootUrl"/> | |
<c:url value="/resources" var="resourcesUrl"/> |
b. Imports
The following section imports a number of CSS and JavaScript resources which includes the core jQuery library, the DateJS library (a Date utility, http://www.datejs.com/), DataTables (a jQuery plugin, http://datatables.net/), and a custom jQuery plugin for retrieving records via AJAX and inserting records to a table automatically:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- CSS Imports--> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/jquery/dark-hive/jquery-ui-1.8.6.custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/datatables/custom.css"/> | |
<link rel="stylesheet" type="text/css" media="screen" href="${resourcesUrl}/css/main/main.css"/> | |
<!-- JS Imports --> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-1.5.2.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/jquery/jquery-ui-1.8.12.custom.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/datejs/date.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/datatables/jquery.dataTables.min.js"></script> | |
<script type="text/javascript" src="${resourcesUrl}/js/util/util.js"></script> |
c. Menu
The following section declares a menu list:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="menu"> | |
<ul> | |
<li><a href="${rootUrl}event">Events (DataTables)</a></li> | |
<li><a href="${rootUrl}jqgrid/event">Events (jQgrid)</a></li> | |
<li><a href="${rootUrl}error">Errors</a></li> | |
<li><a href="${rootUrl}monitor/event">Monitor Events</a></li> | |
<li><a href="${rootUrl}monitor/error">Monitor Errors</a></li> | |
</ul> | |
<br style="clear:left"/> | |
</div> |
d. Table
The following section creates an empty table and three HTML links (controller links) for adding, editing, and deleting of records. Take note of the table id, eventTable, because this id will be referenced multiple times:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h3 class="title">Events - DataTables Version</h3> | |
<table id='eventTable'> | |
<thead> | |
<tr> | |
<th></th> | |
<th>Id</th> | |
<th>Name</th> | |
<th>Description</th> | |
<th>Participants</th> | |
<th>Date</th> | |
</tr> | |
</thead> | |
<tbody> | |
</tbody> | |
</table> | |
<c:if test="${empty events}">There are currently no events in the list.</c:if> | |
<div class="control"> | |
<span><a href="#" id="addLink">Add</a></span> | |
<span><a href="#" id="editLink">Edit</a></span> | |
<span><a href="#" id="deleteLink">Delete</a></span> | |
</div> |
e. Dialogs
The following section includes four external JSPs. The first three JSPs contain form elements for adding, editing, and deleting of records respectively. The fourth dialog is used for displaying generic messages.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<jsp:include page="/WEB-INF/jsp/dialogs/addDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/editDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/deleteDialog.jsp"/> | |
<jsp:include page="/WEB-INF/jsp/dialogs/genericDialog.jsp"/> |
f. Conversion of links to buttons
The following section converts the previous three links to a button. This is mainly for aesthetic purposes:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(function() { | |
// Convert links to buttons | |
$('#addLink, #editLink, #deleteLink').button(); | |
... | |
... | |
}); |
g. Attaching link functions
The following section attaches a function to our controller buttons. Each function will trigger a dialog box. These dialog boxes are the four dialog JSPs we included earlier:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(function() { | |
... | |
... | |
// Assign a function to addLink | |
// Displays a dialog form for adding a new record | |
$("#addLink").click(function() { | |
// Show the dialog | |
$( "#addDialog" ).dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
return false; | |
}); | |
// Assign a function to editLink | |
// Checks first if a record is selected from the table | |
// Then it retrieves that record via jQuery's data()storage method | |
// Finally it displays a dialog form for editing the selected record | |
$("#editLink").click(function() { | |
var tId = $('input:radio[name=eventRadio]:checked').val(); | |
if (tId == null) { | |
$("#genericDialog").text("Select a record first!"); | |
$("#genericDialog").dialog( | |
{ title: 'Error', | |
modal: true, | |
buttons: {"Ok": function() { | |
$(this).dialog("close");} | |
} | |
}); | |
} else { | |
// Retrieve record | |
var record = null; | |
for (var i=0; i<$('#eventTable').data('records').length; i++) { | |
if ($('#eventTable').data('records')[i].id == tId) { | |
record = $('#eventTable').data('records')[i]; | |
break; | |
} | |
} | |
// Assign record to form fields | |
$('#editForm #ename').val(record.name.toString()); | |
$('#editForm #edate').val(new Date(record.date).toString('yyyy-MM-dd')); | |
$('#editForm #edescription').val(record.description.toString()); | |
$('#editForm #eparticipants').val(record.participants.toString()); | |
// Show the dialog | |
$("#editDialog").dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
} | |
return false; | |
}); | |
// Assign a function to deleteLink | |
// Checks first if a record is selected from the table | |
// Finally it displays a dialog form for deleting the selected record | |
$("#deleteLink").click(function() { | |
// show dialog box | |
var tId = $('input:radio[name=eventRadio]:checked').val(); | |
if (tId == null) { | |
$("#genericDialog").text("Select a record first!"); | |
$("#genericDialog").dialog( | |
{ title: 'Error', | |
modal: true, | |
buttons: {"Ok": function() { | |
$(this).dialog("close");} | |
} | |
}); | |
} else { | |
$("#deleteDialog").dialog({ | |
modal: true, | |
width: 350, | |
close: function(event, ui) { } | |
}); | |
} | |
return false; | |
}); | |
... | |
... | |
}); |
h. Retrieval of records and conversion to DataTables
The following section calls a custom getRecords() function which will retrieve Event records, populate our table with data, and convert the table to DataTables:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(function() { | |
... | |
... | |
// Retrieve all records for the first time | |
// Converts table to DataTable as well | |
$.getRecords('#eventTable', '${rootUrl}event/getall', | |
['id', 'name', 'description', 'participants', 'date'], | |
function() { | |
$('#eventTable').dataTable( { | |
"bJQueryUI": true, | |
"sPaginationType": "full_numbers" | |
}); | |
}); | |
}); |
If you want to disable DataTables, just change the previous function to the following:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$(function() { | |
... | |
... | |
// Retrieve all records for the first time | |
// Converts table to DataTable as well | |
$.getRecords('#eventTable', '${rootUrl}event/getall', | |
['id', 'name', 'description', 'participants', 'date'], | |
null); | |
}); |
Results
When we run the application, the result should be similar to the following image (this is an actual screenshot taken from a live deployment):Limitations
Our DataTables page has some limitations. However, these are not DataTables limitations but rather something that we intentionally (and unintentionally) did not implement:- The status "Showing 1 to 4 Entries" has a bug when adding or deleting a record. You need to refresh the whole page to show the correct status
- Date conversion is in integer format instead of Date
- UI validation is not implemented
Playground
Some developers might not have time to build the entire project. Maybe they just want something to play around really fast. Because of that, I've deployed live samples to Cloud Foundry and added sample fiddles via JSFiddle.JSFiddle
If you want to explore more about DataTables, I've provided fiddles for you to play around. These fiddles do not need any server-side programs to run. Feel free to fork them.Here are the fiddles:
- Plain table with static data: http://jsfiddle.net/krams/Us9S5/
- Plain table with dynamic data: http://jsfiddle.net/krams/jD67t/
- Add form: http://jsfiddle.net/krams/8QKAe/
- Edit form: http://jsfiddle.net/krams/Kf4MF/
- Full table with DataTables and buttons: http://jsfiddle.net/krams/9Syqc/
What is JSFiddle?
JsFiddle is a playground for web developers, a tool which may be used in many ways. One can use it as an online editor for snippets build from HTML, CSS and JavaScript. The code can then be shared with others, embedded on a blog, etc. Using this approach, JavaScript developers can very easily isolate bugs. We aim to support all actively developed frameworks - it helps with testing compatibility - Source: http://doc.jsfiddle.net/
Cloud Foundry
If you want to tinker with a live deployment, I suggest you visit the application's live site at http://spring-mysql-mongo-rabbit.cloudfoundry.com/eventWhat is Cloud Foundry?
Cloud Foundry is the open platform as a service project initiated by VMware. It can support multiple frameworks, multiple cloud providers, and multiple application services all on a cloud scale platform.
Next Section
In the next section, we will explore jQgrid, a jQuery plugin, for displaying tabular data. Read next.
Share the joy:
![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() |


Hi Krams,
ReplyDeleteThanks for this wonderful post... I started using your example as a template in one of my project.
I have a quick question here. Could you please explain the significance of the arguments - ['id', 'name', 'description', 'participants', 'date'] that you are passing to "getRecords" method. How did you arrive at these arguments. Are they case sensitive... Sorry for
Hi Krams,
ReplyDeleteYou are too good man! You rock on the net!!!
Your tutorials are too good to refer & learn for people learning new technologies. Keep up the good work. My well-wishes are always with you.
You will always succeed in your professional life & reach greater heights because of the knowledge you have shared with so many learners.
God Bless You!
@Prasad, I believe you're referring to the custom JavaScript plugin that I wrote (see https://github.com/krams915/spring-mysql-mongo-rabbit-integration/blob/master/spring-mysql-mongo-rabbit/src/main/webapp/resources/js/util/util.js).
ReplyDeleteYes, they are case-sensitive because JavaScript will use it as a property for your data. These arguments are required because that's where the plugin will get its fields. I arrive these arguments at will because I'm the one who wrote the plugin. For a robust table, I suggest you use the jQgrid.
@Anonymous, thanks for the comments. It's my pleasure to share. Remember "freely you receive, freely you give" :) I still have more articles to publish. I'm just juggling my time from some various activities.
ReplyDeleteThanks for sharing your info. I really appreciate your efforts and I will be waiting for your further write ups thanks once again.
ReplyDeleteorg.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
ReplyDeleteinformative blog, keep posting java classes in pune
ReplyDelete