Fun with Ext JS and Aptana Jaxer
Tuesday, June 10th, 2008
Shortly after Aptana released Jaxer, a new server-side JavaScript platform, I was able to spend some time running Ext JS code on the server-side. Jaxer facilitates a tightly knit integration between the client and server by allowing you to include JS code that will be run on the server, client, both, or as a server-proxy. The server-proxy allows Jaxer to wrap client and server-side communications up allowing either synchronous or asynchronous calls between the client and server. Jaxer provides a means for file, database or even socket access as one would expect from a server-side platform. Check out Aptana Jaxer for more details on how you can leverage your JavaScript skills on the server-side.
Jaxer Store and the Ext Grid
Download the complete source for these examples if you would like to setup the code on your own Jaxer server. This first example shows off a very simple wrapper around an Ext.data.Store and the corresponding Jaxer server code.
JaxerStoreServer.js contains a simple server side function that builds and executes a query. It assumes that you have already properly configured Jaxer’s config.js to point to the corresponding database. Notice that no server-side post processing is involved as it natively returns a JSON object.
function ExtJaxerProxy(params) { var fld = [], q = []; var fields = params.fields; for (var i = 0; i < fields.length; i++) { if (typeof fields[i] == 'string') { fld.push(fields[i]); q.push('?'); } else if (typeof fields[i] == 'object') { fld.push(fields[i].name); q.push('?'); } } var qp = fld; var query = 'SELECT ' + fld.join(',') + ' FROM ' + params.table; if (params.sortInfo) { query += ' ORDER BY ' + params.sortInfo.sort + ' ' + params.sortInfo.dir; qp.push(params.sortInfo.sort); qp.push(params.sortInfo.dir); } if (params.start && params.limit) { query += ' START ' + params.start + ' LIMIT ' + params.limit; qp.push(params.start); qp.push(params.limit); } return Jaxer.DB.execute(query); }
JaxerStore.js contains a very simple Ext data store to deal with connecting to Jaxer. This utilizes the built in ‘Async’ function that Jaxer wraps around all functions contained in a ’server-proxy’ include.
Ext.data.JaxerStore = function(config) { var params = Ext.apply({ fields : config.fields, table : config.table }, config.baseParams || {}); Ext.data.JaxerStore.superclass.constructor.call(this, Ext.applyIf(config, { reader : new Ext.data.JsonReader(Ext.apply({ root : 'rows' }, config.readerConfig), config.fields) })); ExtJaxerProxyAsync(this.loadData.createDelegate(this), params); }; Ext.extend(Ext.data.JaxerStore, Ext.data.Store);
Make sure both files are being included on your page, along with Ext.JaxerStore should be running in the client and JaxerStoreServer should be running as a ’server-proxy’. With the Jaxer store and server proxy in place, it’s only a matter of creating an instance of the store and hooking it up to a component that can use the data. Here we create the Jaxer store (notice that it now requires an additional table parameter), then we create a simple GridPanel and pass along our new-fangled JaxerStore.
Ext.onReady(function() { var store = new Ext.data.JaxerStore({ table : 'demo', fields : [ {name : 'name'}, {name : 'phone'}, {name : 'email'} ], readerConfig : { sortInfo : { sort : 'name', dir : 'asc' } } }); // create the Grid var grid = new Ext.grid.GridPanel({ store : store, columns : [ {header : "Name", sortable : true, dataIndex : 'name'}, {header : "Phone #", sortable : true, dataIndex : 'phone'}, {header : "Email", sortable : true, dataIndex : 'email'} ], viewConfig : { forceFit : true }, stripeRows : true, height : 350, width : 680, title : 'Jaxer Demo Grid', renderTo : Ext.getBody() }); });

Server-side Ext.(X)Template
This second example shows off actually running some Ext code on the server-side to take advantage of Ext’s template system. For this example I changed the JS includes for ext-base and ext-all to include a runat=”both” attribute so that the Ext library is available on the client and the server side. The html page includes a simple empty div with an id of ‘posts-main’ and a call to window.onserverload = loadPosts(). The loadPosts function simply selects some data from the ‘posts’ table in the database, then runs that data through the Ext.XTemplate. The XTemplate then loops through the rows array that Jaxer queries provide and will create the div/h2/p structure for each row. A quick formatting function lets us add a nice little ‘more’ link for those posts that are too long.
function loadPosts() { var vals = Jaxer.DB.execute('select id, title, body, perm from posts'); var tpl = new Ext.XTemplate('<tpl for="rows">', '<div id="post-{id}" class="post">', '<h2><a href="/jxtest/post/{perm}">{title}</a></h2>', '<p class="post-body">{body:this.formatBody}</p>', '</div>', '</tpl>', { formatBody : function(val, all) { if (val.length > 300) { return Ext.util.Format.ellipsis(val, 300) + '<a href="/jxtest/post/' + all.perm + '">Read More »</a>'; } else { return val; } } }); tpl.overwrite('posts-main', vals); }

In Conclusion
We can see that Jaxer lets developers leverage the hard work which has already been spent building client-side libraries on the server-side. These simple examples show off some of the true potential of utilizing the Ext JS framework on the server-side.
Download the source code for the examples in this post.

