Of ExtJS’s ScriptTagProxy, Spring Actions, TreePanels and JsonStores

I recently had to implement an Organization lookup (backed by a LDAP repository) widget that will be used by some of the web applications I developed and this in a cross-domain environment.  The data displayed within the widget is broken down in Organization->Group->Person hierarchy and on selection of a Person and click of a button, a callback would sent some information about the selected person to a function in the calling application.

The  organizational hierarchy was implemented logically with a tree using an ExtJS TreePanel component. The challenge then became to propertly load this TreePanel. The ExtJS documentation specifies that a  “TreePanel must have a root node before it is rendered“,  which can be done by specifying the ‘root’ property, or using the setRootNode method. For the purpose of my widget, i needed the tree structure to be completely rendered with parent nodes already expanded to reveal children. What needed was to create this hierarchy in my Spring controller and return it to be loaded in my TreePanel. The Ext.data.ScriptTagProxy component was the key in allowing me to make the cross domain call the widget needs to retrieve the Tree data (Check this article if you are using IE6 and getting a Javascript error when using ScriptTagProxy).

Let’s get in the code, first starting with the Store definition:

var companyStore = new Ext.data.Store({

 autoLoad: true,
 //JSONP Proxy Setup
 proxy: new Ext.data.ScriptTagProxy({
 url: getCompanyTreeURL
 }),
 reader: new Ext.data.JsonReader(
 {
 //I named my json object root's 'node', just for the sake of it
 root: 'node',
 totalProperty: 'totalCount',
 idProperty: 'id',
 fields:
 [
 //Notice that these are TreeNode regular properties
 'id',
 'text',
 'children',
 'expanded',
 'leaf'
 ]
 }),
 listeners:
 {
 load: function(store, recs)
 {
 //Once the data is loaded from the backend, i can draw my tree, with the root node
 //already created by the backend
 var companyTree = new Ext.tree.TreePanel(
 {
 useArrows: true,
 autoScroll: true,
 animate: true,
 renderTo:'tree-div',
 enableDD: true,
 containerScroll: true,
 border: false,
 rootVisible: false,
 root: new Ext.tree.AsyncTreeNode(store.reader.jsonData.node[0])

 });
 }
 }
 });

This is an example of the JSON data that will be created manually in the backend and sent to the company store, and
then finally loaded in the TreePanel root node. Notice the stcCallback1001 function that the data is wrapped in? This is required when you are using a ScriptTagProxy, and you will need to manually do that wrapping in your backend code.

stcCallback1001(
{
 'node':
 [
 {
 'id':1,
 'children':
 [
 {
 'text':'Big Bad Company',
 'id':'222',
 'expanded':true,
 'children':
 [
 {
 'text':'Little Bad Company 1',
 'id':'1005',
 'leaf':true
 },
 {
 'text':'Little Bad Company 2',
 'id':'1010',
 'leaf':true
 }
 ]
 }
 ,
 {
 'text':'Bigger Bad Company',
 'id':'123',
 'expanded':true,
 'children':
 [
 {
 'text':'Little Bigger Bad Company',
 'id':'23423',
 'leaf':true
 }

 ]
 }
 ]
 }
 ]
})

Now switching to setting things up on the server side. If you want to ScriptTagProxy paradigm to work, your response content type needs to be set to “text/javascript” as it will be interpreted as such on the client side.


 @RequestMapping("getCompanyTree.json")
 public void getCompanyTreeList(@RequestParam("callback") String callback, HttpServletResponse response)
 {
 Company root = directoryService.getCompanyTree();
 response.setContentType("text/javascript");
 try
 {
 OutputStreamWriter os = new OutputStreamWriter(response.getOutputStream());
 String companyData = getCompanyNodes(root);
 //The callback parameter value was sent by the ScriptTagProxy object in the UI, we use it to wrap the data in the function call
 os.write(callback + "(");
 os.write("{'node':[{'id':1, " + companyData + "}]}");
 os.write( ")");
 os.flush();
 os.close();
 }catch (IOException e)
 {
 e.printStackTrace();
 }

 }

Still in the controller, this function will actually build out the tree structure in a string format using recursion:


public String getCompanyNodes(Company rootCompany)
 {
 String returnValue = new String();
 boolean firstNode = true;
 returnValue += "'children':[\n";
 for(Company comp : rootCompany.getChildren())
 {
 if(!firstNode)
 {
 returnValue += ",\n";
 }
 else
 {
 firstNode = false;
 }
 returnValue += "{\n";
 returnValue +="'text':'" + comp.getName() + "',\n" +
 "'id':'" + org.getId() + "',\n";
 if(comp.getChildren().size() > 0)
 {
 returnValue += "'expanded':true,\n";
 //A little recursion saves us a few lines of code
 String childrenNodeReturnValue = getCompanyNodes(org);
 returnValue += childrenNodeReturnValue;

 }
 else
 {
 returnValue += "'leaf':true\n";
 }
 returnValue += "}\n";;
 }
 returnValue += "]";
 return returnValue;
 }

There you have it. Hope it helps!

Advertisements

One thought on “Of ExtJS’s ScriptTagProxy, Spring Actions, TreePanels and JsonStores

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s