Friday, September 21, 2007

Grails Ajax Chained Drop Down Select Boxes

I love grails. I recently started developing a new application for a new employer and one of the requirements of the application was a chained drop down select between a Building drop down list and a Room drop down list. Since this is my first month playing with Grails it took a little while for me to put this functionality together (3 1/2 hours). I am sure it would have taken longer if I had been using my old Spring, Hibernate, Struts, Ant, Xdoclet, Sitemesh combination.

By the way, I am assuming that you have already installed grails and have grails create-app 'ed an application. My application is named "inventory". I am using grails version 0.6 that is downloadable from the grails download site.

Step 1 - Creating my Domain Classes (grails create-domain-class)

I use eclipse Europa for my development environment and I am currently working on a windows box with a windows command line.
First, I created a Building.groovy class using grails create-domain-class command.
Next, I create a Room.groovy class using the grails create-domain-class command.
The grails command created a new Building.groovy and Room.groovy class under my application's grails-app/domain folder it also created a BuildingTests.groovy and a RoomTest.groovy under the test/integration folder (I won't be going over the testing of these domain classes because it is soooooo easy in Grails! RTM)

Step 2 - Modifying my Building Domain Class

I refresh my inventory application using F5 in eclipse and voila! There are my two new groovy classes Building.groovy and Room.groovy. Now, I need to add fields to my classes, a one-to-many relationship between Building and Room, and also add constraints on both of my classes. Sounds like a lot of work, but it really isn't.

For the Building class I add the following fields:
String buildingNumber
String buildingName
String addressLineOne
String addressLineTwo
String city
String state
String zipCode
I also know that the Building class has many Room classes. So I setup that relationship too. What this does is create a property of type Set that is mapped through Hibernate to the Room based on the primary key of Building (which incidentally is named "id"). No messing with Hibernate XML files!
static hasMany = [rooms : Room]
I want my Building class to look pretty when I display the drop down list on the view so I go ahead and add a toString method to the Building class right away.
String toString() {
return "$buildingName ($buildingNumber)"
}
Finally, I need to add my constraints to the Building class so that user's can't save an invalid Building to the database. These constraints aren't production ready for this application. I will clean them up later to make sure that the data is 100% valid per the business rules at my organization.
static constraints = {
buildingNumber(unique:true,blank:false)
buildingName(blank:false)
addressLineOne(blank:false)
addressLineTwo()
city(blank:false)
state(blank:false)
zipCode(blank:false)
rooms()
}

* At this point I usually run the grails console and save a few Buildings to the underlying database to make sure everything is working OK. Then I develop some tests. Then I move on.

Step 3 - Modifying my Room Domain Class

Time to modify my Room class. I add the limited amount of fields as follows:

String roomNumber
String notes

Building building

Next, I add belongsTo so that Room knows it belongs to a Building.
static belongsTo = Building
I want my Rooms to look pretty in the view when I show them in a drop down list so I add a toString method to the Room class like so:
String toString() {
def s = this.roomNumber
if(notes){
s += " - $notes"
}
return s
}
Now I need to add my limited constraints. Again, there is a lot more you can do with grails constraint validations:
static constraints = {
roomNumber(blank:false)
notes()
}

I do some quick testing using grails console and write up some quick unit tests and I am off to my next step.

Step 4 - Creating Controllers

Now that I have my domain classes in place I want to put together a controller so that I can see my work in a browser. I type in "grails create-controller" at the console and when the grails command asks for a name I type in "Building". This creates a BuildingController.groovy in the grails-app/controllers folder of my inventory application. It also creates a BuildingControllerTests.groovy in the test/integration folder of my inventory application.
Once again, I type in "grails create-controller" at the console and when the grails command asks for a name I type in "Room". This creates a RoomController.groovy in the grails-app/controllers folder of my inventory application. It also creates a RoomControllerTests.groovy in the test/integration folder of my inventory application.
I refresh my inventory application in eclipse (F5) and navigate to my grails-app/controllers folder and find my two newly created controllers.
Opening up the Building controller shows me the following:
class BuildingController {
def index = { }
}
Opening up the Room controller shows me the following:
class RoomController {
def index = { }
}
Step 5 - Scaffolding the Domain Classes and Running the Application

I want to see how my two Domain classes are going to look in a browser and start playing with a web implementation of these classes. I use the Grails scaffolding feature to get an idea of what my front-end will look like and how it will behave.
In the grails-app/controllers folder I open the BuildingController.groovy.
It currently contains a single closure, "def index = { }".
I replace the closure "def index = { }" with:
def scaffold = Building
In the grails-app/controllers folder I open the RoomController.groovy.
It currently contains a single closure, "def index = { }".
I replace the closure "def index = { }" with:
def scaffold = Room
Now I can go to my command line and type "grails run-app" and navigate to http://localhost:8080/inventory and see two links. One to the BuildingController and one to the RoomController.