Working with GXT Grid : Add Remote Pagination Functionality

As promised previously today I am going write about adding remote pagination functionality with GXT Grid which was one of most demanded one from my readers. In my previous blog I have described in detail how you can load data from a remote data store and show them in GXT Grid. In this writing the same Comments entity and CommentModel are used to represent data. You will find the code of these classes here.

At first in the implementation of your GWT RPC Service add a method to load all the comments from the data store.

public List<Commentmodel> getAllComment() 
{
   List<Commentmodel> commentList = new ArrayList<Commentmodel>();
   PersistenceManager pm = PMF.get().getPersistenceManager(); 
   try {
      String query = "select from " + Comments.class.getName()+" order by  postedDate desc"; 
      List<Comments> list = (List<Comments>) pm.newQuery(query).execute();
      if (!list.isEmpty()) {
         for (Comments c : list)
         {
            //convert from entity object to DTO
            commentList.add(CommentConverter.entityToModel(c));
         }
      }
   }catch (Exception ex) {}
   finally {
      pm.close();
   }
   return commentList;
}

Now add a method public PagingLoadResult<Commentmodel> getComments(PagingLoadConfig config) in your service which takes a PagingLoadConfig object to determine the limit and offset value of the request and returns the desired list for the paging loader. Here is the implementation of the method.
@Override
public PagingLoadResult<Commentmodel> getComments(PagingLoadConfig config) {

//comments is a private variable of the service implementation class
//private List<Commentmodel> comments;
comments = getAllComment();

//get all the comments from the data store
//and sort this list according to sorting info

if (config.getSortInfo().getSortField() != null) {
final String sortField = config.getSortInfo().getSortField();
if (sortField != null) {
Collections.sort(comments, config.getSortInfo().getSortDir().comparator(new Comparator<Commentmodel>() {
public int compare(CommentModel c1, CommentModel c2) {
if (sortField.equals("comments")) {
return c1.getComments().compareTo(c2.getComments());
} else if (sortField.equals("postedBy")) {
return c1.getPostedBy().compareTo(c2.getPostedBy());
} else if (sortField.equals("postedDate")) {
return c1.getStartingDate().compareTo(c2.getStartingDate());
}
return 0;
}
}));
}
}

//Create a sublist and add data to list according
//to the limit and offset value of the config

ArrayList<Commentmodel> sublist = new ArrayList<Commentmodel>();
int start = config.getOffset();
int limit = comments.size();
if (config.getLimit() > 0) {
limit = Math.min(start + config.getLimit(), limit);
}
for (int i = config.getOffset(); i < limit; i++) {         sublist.add(comments.get(i));       }       
return new BasePagingLoadResult<Commentmodel>
(sublist, config.getOffset(), comments.size());
}

Your server side coding is done. Let's come to client side coding and see how to use this service to add remote pagination functionality with GXT Grid.

First create a RpcProxy object, proxy to make RPC call using the load configuration. With the proxy object create a PagingLoader, loader which is required to load page enabled set of data and enable the remote sorting attribute of the loader.

RpcProxy<PagingLoadResult<CommentModel>> proxy = new RpcProxy<PagingLoadResult<CommentModel>>() {
@Override
public void load(Object loadConfig,
AsyncCallback<PagingLoadResult<CommentModel>> callback) {
Gxtexamplegalary.greetingService.getComments(
(PagingLoadConfig) loadConfig, callback);
}
};

// loader
final PagingLoader<PagingLoadResult<ModelData>> loader = new BasePagingLoader<PagingLoadResult<ModelData>>(
proxy);
loader.setRemoteSort(true);

Now use this loader to create a ListStore of CommentModel  and bind the loader with a PagingToolBar.

ListStore<CommentModel> commentList = new ListStore<CommentModel>(loader);

final PagingToolBar toolBar = new PagingToolBar(3);
toolBar.bind(loader);

Create a List of ColumnConfig  and a ColumnModel from the ColumnConfig list.

List<ColumnConfig> configs = new ArrayList<ColumnConfig>();
ColumnConfig column = new ColumnConfig();
column.setId("comments");
column.setHeader("Comments");
column.setWidth(200);
configs.add(column);

column = new ColumnConfig("postedBy", "Posted By", 150);
column.setAlignment(HorizontalAlignment.LEFT);
configs.add(column);

column = new ColumnConfig("postedDate", "Posting Date", 100);
column.setAlignment(HorizontalAlignment.RIGHT);
column.setDateTimeFormat(DateTimeFormat.getShortDateFormat());
configs.add(column);

ColumnModel cm = new ColumnModel(configs);

Finally create a Grid with the commentList and the column model. Add a Listener with the Grid to handle the remote pagination functionality. In the handleEvent method of the Listener first create a PagingLoadConfig, config and set offset, limit, sort field and sort direction value of the config. Then load data by the loader with this configuration.
final Grid<CommentModel> grid = new Grid<CommentModel>(commentList, cm);
grid.setStateId("pagingGridExample");
grid.setStateful(true);
grid.addListener(Events.Attach, new Listener<GridEvent<CommentModel>>() {
public void handleEvent(GridEvent<CommentModel> be) {
PagingLoadConfig config = new BasePagingLoadConfig();
config.setOffset(0);
config.setLimit(3);

Map<String, Object> state = grid.getState();
if (state.containsKey("offset")) {
int offset = (Integer) state.get("offset");
int limit = (Integer) state.get("limit");
config.setOffset(offset);
config.setLimit(limit);
}
if (state.containsKey("sortField")) {
config.setSortField((String) state.get("sortField"));
config.setSortDir(SortDir.valueOf((String) state
.get("sortDir")));
}
loader.load(config);
}
});
grid.setLoadMask(true);
grid.setBorders(true);
grid.setAutoExpandColumn("comments");
grid.setStyleAttribute("borderTop", "none");
grid.setStripeRows(true);

ContentPanel cp = new ContentPanel();
cp.setBodyBorder(false);
cp.setHeading("Grid with Pagination");
cp.setButtonAlign(HorizontalAlignment.CENTER);
cp.setLayout(new FitLayout());
cp.setSize(700, 300);
cp.add(grid);
cp.setBottomComponent(toolBar);
RootPanel.get().add(cp);

That's all for today. Enjoy GWT and GXT :-)

23 comments:

M. Toulousain September 2, 2010 at 6:56 AM  

Simple question: how do you manage errors?
On a non-paged, you define your own callback with the onFailure method.
Here, the callback is provided by the framework.
How do you known that the load method has worked as expected? (I'm currently working on some errors when the session times out ...).

Unknown September 5, 2010 at 7:46 PM  

@bfabien: I use a client side exception class.
The class will be like this

public class ClientException extends Exception implements Serializable
{

private static final long serialVersionUID = 1L;
private String message = "";

public ClientException()
{

}
public ClientException(String message)
{
super(message);
this.message = message;
}

public ClientException(Throwable cause)
{
super(cause);
}
@Override
public String getMessage()
{
return message;
}

public void setMessage(String message)
{
this.message = message;
}

}

gwtnewbie October 7, 2010 at 6:02 PM  

Hi Shams,

I'm new to GWT/GXT and have found your blog quite helpful... thanks! However, I don't quite understand how to use a client side exception class in this case and print out an error message if an error is encountered during the load. Could you give an example? Thank you in advance for your help!

Gabejazz November 14, 2010 at 4:12 PM  

Hi Shams, congrats for your work. I'm implementing a grid, but I have a problem here.

Look, I have a submit button, a grid and an export button (for export the grid to csv file). When I push the submit button, my grid is refreshed and the button is enabled just when the grid retrieves more than 1 model data (register).

But, I don't how to get the grid dataset count. I tried to get the loader count, but when I inspect this object the count is = 0 and same thing when I get the grid store count.

How can you get this dataset count?

Thanks =]

Unknown November 14, 2010 at 7:04 PM  

ListStore count should work

Carmelo Saffioti April 6, 2011 at 3:34 AM  

Thank you for this great and useful posts! :)

Anonymous April 14, 2011 at 6:52 AM  

Hi Shams, this is a great post.
I need a help.
I need to pass some of my custom data, inside the BasePagingLoadResult, and hence I extended this class (BasePagingLoadResult), and wrote my own setter and getter method.
But in the client side, we are not implementing the AsyncCallback. I mean, I can retrieve my data in onSuccess() method, but here, we are creating a proxy and that proxy is being passed to the loader.
So, in such case, can you please suggest me, what should I do, to retrieve my custom data?

Anonymous May 9, 2011 at 3:55 AM  

The class extending BaseModel which is attached to the grid cannot be used in the RPC call. If we try to use it we get the serialization exception stating that the serialization policy donot allow this type to be serialized.
So how should I handle this?


Thanks,
Madhur

Unknown May 9, 2011 at 7:49 PM  

@Madhur: If you are using any property of your own data type, make sure that your class is serializable. First try with only basic data type...

Unknown May 10, 2011 at 10:28 AM  

Hey Shams,
I had not included a default constructor in the serializable class.

Unknown May 10, 2011 at 7:39 PM  

Yes Madhur, default constructor is one of the requirement to make a class serializable.

Mehdi May 14, 2011 at 3:11 PM  

Wondelful tutorial! keep on with the good stuff.

The Scientist June 1, 2011 at 3:42 AM  

you are great Bro, by the way, what the purpose of this part of code String sortField = config.getSortInfo().getSortField();
thx

Unknown June 1, 2011 at 7:48 PM  

If you want to get sorted data, you need to use the sortField.

bhaskarunichiranjeevi October 10, 2011 at 10:31 PM  

Hi can u kindly provide me with an info how can i use retrieved data into combobox in gxt.
for eg: i will have a form with textfields,the entered data will be added into the grid below the form.When i use to edit a row data i will click on the row and the data will be populated to text fields.The problem is i had a combobox in the form where i cant able to populate value onto combobox while editing.can u please provide me some suggestions.. thank u

Unknown December 29, 2011 at 9:12 AM  

Hi, I am newbie of GWT, and I am trying to learn by using your very helpful tutorials.
In this way, I was able to build a virtual phonebook. It is created by a recordset extracted from a mySQL database.
Subsequently, I add remote pagination to a Grid showing the list of contacts, adding filtering and checkboxes to select one or more contacts at the same time. I successfully added remote paging and filtering to Grid, but now I would like to add grouping. I tried to work with GroupStore object, but unsuccessfully. Is it possible? Could you help me? My private email is matteo.cacciola@gmail.com: if I can contact you privately, I should send my tentative code, since here I cannot

ahimsa January 5, 2012 at 3:21 AM  

hi shams,

i make my own project according to this tutorial and i found this error : com.google.gwt.user.client.rpc.SerializationException: Type 'com.gxt.grid.client.Posting' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = com.gxt.grid.client.Posting@1fbea5e

my Posting.java :
public class Posting extends BaseModel{
private static final long serialVersionUID = 1L;
public Posting() {}
public Posting(int idd, String name){
set("idd", idd);
set("name", name);
}
public String getIdd() {
return (String) get("idd");
}
public void setIdd(int idd) {
set("idd", idd);
}
public String getName() {
return (String) get("name");
}
public void setName(String name) {
set("name", name);
}

i've got that error when execute new BasePagingLoadResult(sublist, config.getOffset(),posts.size());

any advice to solve this thing?
thanks before.

Unknown July 26, 2012 at 5:13 AM  

Thanks For Your Tutorial..

Perfectly working..

Maddalena May 2, 2013 at 7:25 AM  

To find out how to manage errors, check out this link (I've tested it myself - it works!)

Zaenal May 23, 2013 at 7:18 PM  
This comment has been removed by the author.
Unknown April 14, 2014 at 11:03 AM  

Hi Ahimsa,


in serviceImpl,

use ModdelData as ur PagingLoader object

public PagingLoadResult getQuestionnaireDatas(PagingLoadConfig config, String region) throws SessionExpiredException
{
try
{
ApprovalData result = new ApprovalData();
ListStore questionnaireDatas = null;

result = delegate.getQuestionnaireApproval(getSessionContext().getUser().getUserId(), region);

if(result != null && result.getQuestionnaires() != null && result.getQuestionnaires().size() > 0)
{
questionnaireDatas = new ListStore<>();

questionnaireDatas.add(result.getQuestionnaires());
//Collections.sort(result.getQuestionnaires(), new QuestionnaireData());
}

ArrayList sublist = new ArrayList();
int start = config.getOffset();
int limit = questionnaireDatas.getModels().size();
if (config.getLimit() > 0)
{
limit = Math.min(start + config.getLimit(), limit);
}
for (int i = config.getOffset(); i < limit; i++)
{
sublist.add(questionnaireDatas.getModels().get(i));
}

return new BasePagingLoadResult(sublist, config.getOffset(), questionnaireDatas.getModels().size());
}
catch(SessionExpiredException ex)
{
throw new SessionExpiredException();
}
catch (Exception e)
{
TECH_LOG.error("error",e);
}
return null;
}

EDOblog October 10, 2014 at 1:10 AM  

Hello, I'm new to java, and I can't resolve BasePagingLoadResult, witch package I've to choose?

Unknown April 28, 2015 at 2:15 AM  

Hey
I have problem with pagination in GXT 3.1.1 in comboBox under IE 10

When I click next, previous, last, first button pagination values hiddes

This is similar problem:
http://www.sencha.com/forum/showthread.php?144219-GXT-2.2.4-Combobox-with-paging-loader-popup-disappear-problem

Can you saw this problem in your experience?

Total Pageviews

Tags

Twitter Updates
    follow me on Twitter

    Followers