Friday, December 28, 2012

Jquery JTable with Java and Spring

I just finished a new project working with Jtable to create some crud tables. I was pretty happy with the tool but made numerous extension to the framework which I'll look at today, I'll also provide some java spring mvc examples which I was unable to find online as all the documentation was for asp.net.

Why don't I start with a list of the features I've added:

  • Client side sorting with tablesorter
  • New hidden-edit and hidden-create types
  • Custom up/down arrows for sorting
  • Date Format fix for a date that is just a number ie: doesn't have format Date(1320259705710)
  • Removed cancel buttons so you have to close the dialog with the X in the top corner
  • Use an add record button that is outside the table (default is in the footer row)
  • Default null date to empty string rather than today
  • Loading dialog is optional
  • Option to show error message in table row rather than in popup
  • Added expand contract buttons for the child table
  • Added date time formatting (default is only date)
  • Allow hidden header row (handy for child tables)
  • Added grouping of data by a column (data must be sorted by the group)

Lets start by looking at some of the java code in the spring controller, and then move on to the jtable extensions:

This is the search method:
 
@RequestMapping(value = "/custSearch.do", method = RequestMethod.POST)
@ResponseBody
public JsonJtableResponse search(@ModelAttribute CustSearch searchFilters) {
 //check all search filters are not empty
 if (searchFilters == null || searchFilters.isEmpty()) {
  return new JsonJtableResponse().error("At least one filter must be specified");
 }

 List customerList = customerService.geCustByFilter(searchFilters);

 return new JsonJtableResponse().ok(customerList);
}
Next we have the insert new record:
 
@RequestMapping(value = "/insertCust.do", method = RequestMethod.POST)
@ResponseBody
public JsonJtableResponse insert(@ModelAttribute Customer customer, BindingResult result) {
 if (result.hasErrors()) {
  return new JsonJtableResponse().error(result.getAllErrors());
 }
 try {
  Customer newCust = customerService.insertCust(customer);
  return new JsonJtableResponse().ok(newCust);
 } catch (Exception e) {
  return new JsonJtableResponse().error(e.getMessage());
 }
}
Then the update record:
@RequestMapping(value = "/updateCust.do", method = RequestMethod.POST)
@ResponseBody
public JsonJtableResponse update(@ModelAttribute Customer customer, BindingResult result) {
 if (result.hasErrors()) {
  return new JsonJtableResponse().error(result.getAllErrors());
 }
 try {
  customerService.updateCust(customer);
  return new JsonJtableResponse().ok();
 } catch (Exception e) {
  return new JsonJtableResponse().error(e.getMessage());
 }
}
Finally the delete:
@RequestMapping(value = "/deleteCust.do", method = RequestMethod.POST)
@ResponseBody
public JsonJtableResponse delete(@RequestParam Integer custId) {
 try {
  customerService.deleteCust(custId);
  return new JsonJtableResponse().ok();
 } catch (Exception e) {
  return new JsonJtableResponse().error(e.getMessage());
 }
}
You've probably noticed that I've wrapped the jtable api with the JsonJtableResponse object. You can download this handy helper class here.

 As for the jsp, all you really need is a div for the jtable:
<!-- Jtable Dependencies -->
<link rel="stylesheet" type="text/css" href="/css/jquery/jtable.css"/>
<link rel="stylesheet" type="text/css" href="/css/jquery/validationEngine.jquery.css"/>
<script type="text/javascript" src="/scripts/jquery.jtable.js"></script>
<script type="text/javascript" src="/scripts/jquery.validationEngine.js"></script>
<script type="text/javascript" src="/scripts/jquery.validationEngine-en.js"></script>
<script type="text/javascript" src="/scripts/jquery.tablesorter.min.js"></script>

<script type="text/javascript" src="/scripts/manageCust.js"></script>

<%-- Built with jtable --%>
<div id="custSearchResultsDiv" class="searchResultsDiv">
 <div style="overflow: hidden; margin-top: 5px; margin-bottom: 5px;">
  <div class="jtable-rowCount"></div>
 </div>
 <div class="buttonDiv">
  <input id="custInsertButton" type="button" class="but searchButton jtable-add-record" value="Insert New Customer"/>
 </div>
</div>
The real work is done in the manageCust.js this is where the table structure is defined and the controller urls get configured:
$(document).ready(function() {
 
 $("#custSearchButton").click(function() {
  initResultsTable();
 });
 
 //setup the jtable that will display the results
    $('#custSearchResultsDiv').jtable({
        paging: false,
        sorting: false, //this is an ajax sort
        clientSort: true, //this needs jquery.tablesorter.min.js
        columnResizable: false,
        columnSelectable: false,
        selecting: false,
        multiselect: false,
        selectingCheckboxes: false,
        
        actions: {
            listAction: baseUrl + '/admin/searchCust.do',
            createAction: baseUrl + '/admin/insertCust.do',
            updateAction: baseUrl + '/admin/updateCust.do',
            deleteAction: baseUrl + '/admin/deleteCust.do'
        },
        fields: {
         custId: {
                key: true,
                create: false,
                edit: false,
                list: false
            },
            name: {
                title: 'Full Name',
                width: '30%',
                inputClass: 'validate[required]',
                type: 'hidden-edit' 
            },
            birthYear: {
                title: 'Birth Year',
                width: '15%'
            },
            employer: {
                title: 'Employer',
                width: '25%'
            },
            infoAsOfDate: {
                title: 'As Of Date',
                type: 'date',
                width: '15%',
            },
            disabled: {
                title: 'Status',
                type: 'checkbox',
                values: { 'false': 'Active', 'true': 'Disabled' },
                defaultValue: 'false',
                width: '15%',
                listClass: 'center'
            }
        },
        //Initialize validation logic when a form is created
        formCreated: function (event, data) {
            data.form.validationEngine();
        },
        //Validate form when it is being submitted
        formSubmitting: function (event, data) {
            return data.form.validationEngine('validate');
        },
        //Dispose validation logic when form is closed
        formClosed: function (event, data) {
            data.form.validationEngine('hide');
            data.form.validationEngine('detach');
        }
    });
    
});

function initResultsTable() {
 
 //perform the search and passin the filters - filters will be bound to the CustomerSearch bean
    $('#custSearchResultsDiv').jtable('load', {
     birthYear: $("#birthYear").val(),
     employer: $("#employer").val()
    }, 
    function() { //load complete
     //alert('table data loaded');
    });
    
}

Above you can see some of the extension I've made; type: 'hidden-edit' allows the Customer Name to be entered when you insert a new customer, but the name is not able to be updated when editing.

I've also added clientSort: true, this allows the data to be sorted on the client side using table sorter which must be in the jsp.

As I'm running out of time I'll just attach my enhanced version of jtable as well as the css I've been using:
Original Jtable (handy to do a compare and extract the new features you need)
My Enhanced Jtable
My new Jtable css

Feel free to use and edit these as needed. I will go into more detail of my enhancements in a future post.


14 comments:

  1. Thanks for the blog.It is very useful.Can you please attach the source code

    ReplyDelete
  2. Sorry I can't attach the whole source at this time as it's all within a clients code base. This blog is not intended to provide fully functional code, just snippets of the important pieces so people can take what they need. If there are specifics you're looking for like the complete controller, I can provide that. Let me know thanks.

    ReplyDelete
    Replies
    1. Thanks a ton for posting such an excellent blog. I am not able to integrate the tablesorter for clientSorting. It would be really very helpful if you can provide some additional details(integration points) about the integration between jTable and tableSorter.

      Delete
    2. Assuming you have a table integrated with jtable, using my enhanced javascript provided above. You just need to download the tablesorter javascript and add it to your project script dir.

      Next include the javascript in your jsp <script type="text/javascript" src="/scripts/jquery.tablesorter.min.js"></script>

      Now update the jtable init parameters like this clientSort: true, and it will work.

      The only issue you might have is missing images which I didn't included in the post. Inside the jtable.css you can see the image urls background: url("../../images/arrow-down.gif") no-repeat right; you will need to place some appropriate images at this location and adjust the url as needed for your project structure.

      To learn more about the integration you can look into the jquery.jtable.js at the _addClientSorting function.

      Delete
  3. Can u please share the code thanks in advance.

    ReplyDelete
  4. which are the imports that I have to do ?

    ReplyDelete
  5. You'll need JQuery for the client side and Spring MVC framework for the server, I'd recommend the getting started guides: http://www.jtable.org/GettingStarted and http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html. Unfortunately setting up a Spring MVC app with JQuery is outside the scope of this tutorial.

    ReplyDelete
  6. can u upload with jars and dependencies

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi! could tell me how to implement this feature?
    "Added grouping of data by a column (data must be sorted by the group)"

    Thanks!!

    ReplyDelete
  9. This comment has been removed by the author.

    ReplyDelete
  10. Sorry I haven't worked with it in quite a while now.

    ReplyDelete
  11. Andrew, do you still have a copy of the javascript code for your enhanced jTable--I am very interested in the enhancments you made for client side sorting. Unfortunately trying to click on the download link above results in a virus warning from Windows Defender. thanks

    ReplyDelete

Note: Only a member of this blog may post a comment.