Using JSON inside grails controllers to do batch transfers

At work I work on an application that uses a mix of velocity and JSF. One of the application required a batch delete from a data table - selecting multiple rows from a data table and batch deleting it on the server.

The legacy velocity implementation for this was nightmare - the form to be submitted was created on the fly and a hidden field populated with a comma seperated list of all id’s to be modified/deleted. Worked rather well but was pain to refactor and maintain. Not to mention fixing bugs on code like this is worse than shooting yourself in the head.

We have implemented the same patter in JSF using a datatable component with Jenia faces selection components. Needless to say all you have to do is create a binding to a collection on the server side and the references are populated on the server during the “apply values” phase. Beautiful - the facade cut the code all the mess.

I was working on another application in grails and wanted to implement a shuttle component. I started by writing a template that worked directly with the domain objects (relying on the toString() and the getId() methods) and took 3 collections. The list of domain objects that went to the left side, the right side and a list of domain objects that cannot be moved. Internally it used two multiple select lists with size=10 Believe it or not - I have reused this same component 4 times without a single line of change.

But the problem was this - when a “select” item is submitted back to the server only the selected ids are passed back and remaining are ignored. There are two ways you can solve this problem - one is to attach a form on-submit event that selects all the items before submitting and the other is to add a hidden field which is updated each time an item is shuttled. I decided to stick to the later. Now came the question of representing this. Would I use a comma separated list of IDs and re parse this on the server. Certainly not a big deal, but its ugly to say the least. I decided to do something else use a combination of prototype JSON support and grails JSON support to make this completely transparent on both the client and the server.

To make this easy - I added a hidden field to the shuttle component (which was gsp template that you just rendered inside a form):

<input type="hidden" value="${right.id.encodeAsJSON()}" name="addedItems" id="addedItems" />

In the above code “right” is a list of domain objects that are on the RHS of the shuttle. Note the “encodeAsJSON()” method available to me as a part of the grails eco system. 

Next add the following method when the elements are getting transferrred:

var rightList = $('rightItems');
  for( var i = rightList.options.length - 1; i >= 0; i-- ) {
  	vals[i] = parseInt(rightList.options[i].value);
  }

  $('addedItems').value = $A(vals).toJSON();

In the above code “rightItems” is the id of the select element that contains the added elements. Note the “toJSON()” available to most prototype javascript objects.   

This will update the hidden field with a JSON representation of the array (done using prototype.js). On the server side you can get a java array by using the Grails converters.

import grails.converters.*;
def newIds = JSON.parse(params.addedItems)
...
newIds.each {
    //do something with the ids here ...
}

The really nice thing here is the complete lack of to-from conversion. You are utilizing your frameworks builtin support for JSON to do everything. I have certainly saved myself a bunch of work using this pattern. I have further encapsulated this as a component and made it reusable, works like a charm. 

 

One Comment

  1. ErvinTW:

    Thanks! Nice post.

Leave a comment