ExtJS Combo boxes & Grails
Posted by jt - 01/03/11 at 05:03 pmThe combo boxes in ExtJs are a great web 2.0 component which can be used to create user friendly forms. In this tutorial, I’m going to show you how to create a combo box drop down in ExtJS using Grails for the backend code.
Here is a screen shot of the component. You can click on the drop down and get a list, or start typing for a AJAX list of values from the server.

In ExtJS I created the combo box with this code:
{ xtype: 'combo', name: 'state', width: 110, displayField: 'stateName', hiddenValue: 'stateCode', store: 'StateStore', allowBlank: false, forceSelection: true, triggerAction: 'all' } |
The trick to this code is the ExtJS store. The store is defined as:
StateStore = Ext.extend(Ext.data.JsonStore, { constructor: function(cfg) { cfg = cfg || {}; StateStore.superclass.constructor.call(this, Ext.apply({ storeId: 'StateStore', root: 'records', url: '/state/list', idProperty: 'stateCode', messageProperty: 'message', fields: [ { name: 'stateName' }, { name: 'stateCode' } ] }, cfg)); } }); Ext.reg('stateStore', StateStore);new StateStore(); |
Notice how I’ve used a relative URL in the code. Also important for my example, I’m using ‘stateCode’ for the id property. This will default to ‘id’ if you do not set it here.
The ExtJS code will expect a JSON string something like this from the server:
{"sucess":true, "records":[{"stateName":"North Carolina", "stateCode":"NC"}, {"stateName":"North Dakota","stateCode":"ND"} ]} |
Using Grails makes it incredibly easy to render this response for ExtJS. First off, I recommend setting your application name to a slash in application.properties (not a slash with quotes). This will setup grails to run in the root context of tomcat (8080:/ vs 8080:/yourappname). This will save you some trouble if you’re going to deploy behind Apache in your production env.
My domain class in Grails is pretty simple:
class State { String stateCode String stateName static constraints = { stateCode(unique:true, blank:false, nullable:false) stateName(blank:false, nullable:false) } static mapping = { id column: 'stateCode'} String toString() { return stateCode } } |
I like to load my data in the bootstrap as follows:
if (State.count() == 0) { loadStates() } . . . def loadStates() { println "loading state codes" new State(stateCode: 'AL', stateName: 'Alabama').save() new State(stateCode: 'AK', stateName: 'Alaska').save() new State(stateCode: 'AR', stateName: 'Arkansas').save() new State(stateCode: 'AZ', stateName: 'Arizona').save() new State(stateCode: 'CA', stateName: 'California').save() new State(stateCode: 'CO', stateName: 'Colorado').save() new State(stateCode: 'CT', stateName: 'Connecticut').save() new State(stateCode: 'DC', stateName: 'D.C.').save() new State(stateCode: 'DE', stateName: 'Delaware').save() new State(stateCode: 'FL', stateName: 'Florida').save() new State(stateCode: 'GA', stateName: 'Georgia').save() new State(stateCode: 'HI', stateName: 'Hawaii').save() new State(stateCode: 'IA', stateName: 'Iowa').save() new State(stateCode: 'ID', stateName: 'Idaho').save() new State(stateCode: 'IL', stateName: 'Illinois').save() new State(stateCode: 'IN', stateName: 'Indiana').save() new State(stateCode: 'KS', stateName: 'Kansas').save() new State(stateCode: 'KY', stateName: 'Kentucky').save() new State(stateCode: 'LA', stateName: 'Louisiana').save() new State(stateCode: 'MA', stateName: 'Massachusetts').save() new State(stateCode: 'MD', stateName: 'Maryland').save() new State(stateCode: 'ME', stateName: 'Maine').save() new State(stateCode: 'MI', stateName: 'Michigan').save() new State(stateCode: 'MN', stateName: 'Minnesota').save() new State(stateCode: 'MO', stateName: 'Missouri').save() new State(stateCode: 'MS', stateName: 'Mississippi').save() new State(stateCode: 'MT', stateName: 'Montana').save() new State(stateCode: 'NC', stateName: 'North Carolina').save() new State(stateCode: 'ND', stateName: 'North Dakota').save() new State(stateCode: 'NE', stateName: 'Nebraska').save() new State(stateCode: 'NH', stateName: 'New Hampshire').save() new State(stateCode: 'NJ', stateName: 'New Jersey').save() new State(stateCode: 'NM', stateName: 'New Mexico').save() new State(stateCode: 'NV', stateName: 'Nevada').save() new State(stateCode: 'NY', stateName: 'New York').save() new State(stateCode: 'OK', stateName: 'Oklahoma').save() new State(stateCode: 'OH', stateName: 'Ohio').save() new State(stateCode: 'OR', stateName: 'Oregon').save() new State(stateCode: 'PA', stateName: 'Pennsylvania').save() new State(stateCode: 'RI', stateName: 'Rhode Island').save() new State(stateCode: 'SC', stateName: 'South Carolina').save() new State(stateCode: 'SD', stateName: 'South Dakota').save() new State(stateCode: 'TN', stateName: 'Tennessee').save() new State(stateCode: 'TX', stateName: 'Texas').save() new State(stateCode: 'UT', stateName: 'Utah').save() new State(stateCode: 'VA', stateName: 'Virginia').save() new State(stateCode: 'VT', stateName: 'Vermont').save() new State(stateCode: 'WA', stateName: 'Washington').save() new State(stateCode: 'WI', stateName: 'Wisconsin').save() new State(stateCode: 'WV', stateName: 'West Virginia').save() new State(stateCode: 'WY', stateName: 'Wyoming').save(flush: true) } |
Here is the closure code for my StateController class. This will get invoked for requests to /state/list.
def list = { def returnMap = [:] def recordList = [] returnMap.sucess = true try { if (params.query) { State.findAllByStateNameIlike("%${params.query}%").each { recordList << [stateName: it.stateName, stateCode: it.stateCode] } } else { State.list().each { recordList << [stateName: it.stateName, stateCode: it.stateCode] } } returnMap.records = recordList } catch (e) { returnMap.sucess = false returnMap.message = 'Error getting state list' log.error("Failed to get state list") log.error(e) } render returnMap as JSON } |
In this, I create a map for my return and set the success property = true. Later you will see that is it trivial to convert a map into a JSON string.
I also create a list variable which I will load with records from my database query, which will get assigned as a property in my return map object.
From ExtJS, I can get either no query value, or a value called query, which is what the user typed. In my example if I do receive a query value (params.query), I use a dynamic finder to find all records in a case insensitive search (State.findAllByStateNameIlike(“%${params.query}%”) ) Otherwise, I just use the .list() method to get all the state records. In either case, I walk the result set and assign map values for each record to the list using the shift operator.
The last part of the magic is using render returnMap as JSON. This converts my map into a JSON object for ExtJS.
Cheers,
JT
A bunch of random technology stuff that has my attention. I work with a lot of Oracle, Java, and dabble with various open source software packages.
March 7th, 2011 at 4:01 am
You might want to take a look at the json-rest-api plugin. It does what you described but directly with domain classes and provides all that’s needed to do a read/write backend for Ext.data.JsonStore
March 7th, 2011 at 7:03 am
Took a quick look at it – looks pretty cool. I wasn’t aware of it. Thanks!
April 9th, 2011 at 10:20 am
[...] Combo boxes & Grails Quick example of using a ExtJS Combo Box backed by a Grails datasource…. [full post] jt JT's Blog uncategorizedextjsgrailsjson 0 0 0 0 0 [...]