Saturday, March 12, 2011

jQuery FlexGrid & Spring MVC 3

Recently I have been working on Spring MVC 3. There is inbuilt support for returning JSON data using @ResponseBody annotation. Just make sure that jackson-all-x.x.x.jar is on your classpath. I used jackson-all-1.7.4.jar.This works great.

I wanted to display the results in a tabular format with some basic sorting and paging functionality with AJAX. So I thought I will give a shot at jQuery. jQuery is such an amazing JavaScript library it makes this a lot easier when it comes to working with JavaScript or manipulating DOM.

I found this amazing jQuery plugin, FlexGrid. It was pretty much what I wanted. Though it does not support i18N or themed L&F, it did the job for me.

So I started integrating the pieces. jQuery FlexGrid expects the JSON data to be in a specific format:

   total: (no of rec)
   page : (page no)
   rows : [{id: idVal, cell: [ (col1 value) , (col2 value) ,.. ]},
           {id: idVal, cell: [ (col1 value) , (col2 value) ,.. ]}
          ]

But the data returned from the method with @ResponseBody annotation is in the following format:

   
{(col1 value) , (col2 value) ,..}

So I thought I might as well go ahead and modify my DAO's to return the data in the required format. But well, why should I modify my DAO's? Its after all a UI layer requirement. This is what I came up with.

Write a wrapper class that will hold the data required for FlexGrid and send this as a JSON response. Here's the wrapper class:

import java.io.Serializable;
import java.util.List;

/**
 * Wrapper class for JSON data to send to the client.
 * 
 * Currently we are using jQuery FlexGrid for displaying JSON data in table.
* * jQuery FlexGrid plug-in requires data to be in the below specified format. * * * total: (no of rec) * page : (page no) * rows : [{id: idVal, cell: [ (col1 value) , (col2 value) ,.. ]}, * {id: idVal, cell: [ (col1 value) , (col2 value) ,.. ]} * ] * * To keep the data service independent of this requirement as far as possible, * the id, cell format specifically ignored. we wrap the result from the data * service and further format the result using JavaScript as required. * * * @author Enterprise Integrals * * @version 1.0 * @see jQuery FlexGrid * * @param T generic data type for list of objects to be sent in the JSON response. */ public class JsonDataWrapper<T> implements Serializable { private static final long serialVersionUID = 1L; //current page private int page; //total number of records for the given entity. private long total; //list of records to be displayed. private List<T> rows; public JsonDataWrapper(int page, long total, List<T> rows) { this.page = page; this.total = total; this.rows = rows; } // getter setter }
Here is how it is used in Spring MVC 3
/**
* FlexGrid submits the following parameters
* page: current page
* rp: rows per page
* sortname: sorting done on which column:
* sortorder: sort order asc/desc
* query: search criteria if you are using search option
* qtype: search on which column in grid. this is again customizable.
* 
* These parameters can be used as filters in the DAO to get the data.
*
* By default the FlexGrid uses POST. You can change to GET.
* @param T the entity list to displayed in grid
*/
@RequestMapping(value="/some Path", method= RequestMethod.POST)
public @ResponseBody JsonDataWrapper<T> getRows(WebRequest request) {
 //get the current page posted from the grid.
 int page = Integer.parseInt(request.getParameter("page"));
 //get the list of objects to be displayed from the db calling the service
 //the total number of pages are dynamically calculated on the basis of the total number of rows in the table. get that.
 JsonDataWrapper<T> jdw = new JsonDataWrapper<T>(page, total rows, rows);
 return jdw;
}
But there is a catch here. The FlexGrid plugin again refuses to render the data. The reason is the List<T> rows is again a list of objects returned from the DAO layer. It does not format the data as required by FlexGrid plugin. How do we do that? So I decided lets not put this logic in our MVC implementation. Here is how the data is set on to the FlexGrid using JavaScript.

 
$("#flex1").flexigrid({
   // standard flexgrid configuration as per your need,
   preProcess: formatData
});

Note the preProcess definition. It pre processes the data before the grid is populated with the JSON data return from the server. You can inline the function that actually formats the data but I kept it separate. Here it goes:

function formatData(data) {
 var rows = Array();
 $.each(data.rows,function(i,row){
                //id can be mapped to any attribute of the return object in the list
  rows.push({id:row.val1, cell:[row.val1,row.val2]});
});
 
 return {
  total:data.total,
  page:data.page,
  rows:rows
 };     
}

Now the grid populated correctly. There are other grid plugins for jQuery but FlexGrid & jQGrid are the best. FlexGrid has lot less functionality as compared to jQGrid but I like it because its light. For advanced features you can have a look at jQGrid.