Halley's Blog

AJAX driven Selects in Grails

Posted in Grails, Technical by Halley on February 19, 2014

A common requirement while creating web applications is the need of an html drop down change the contents of another drop down. I wanted this to be done in Grails. A quick googling took me here to the Grails Wiki.

http://grails.org/AJAX-Driven+SELECTs+in+GSP

But, this was not at all groovy. The code looked like it came from the stone age. Now, after some time, I figured out a futuristic way of the same thing, and that too, in a groovy way!! Lets take a look.

I will be using two domain classes, Country and City. Take a look at them here.

package com.grails.test

class City {
    String name
    static belongsTo = [country:Country]
    }
package com.grails.test

class Country {
    String name
    String abbr
    static hasMany = [cities: City]
    }

Our GSP page will contain two select tags, one which will display the list of countries available and the second one will be updated as we select a country. Listed below is the view component, selectChain.gsp.

<meta name="layout" content="main" />
<div>
	<div>
		<g:select name="country.id" from="${countries}" optionKey="id"
			optionValue="name" noSelection="['':'Select Country']"
			onchange="${remoteFunction (controller: 'country',
                        action: 'findCityForCountry',
                        params: '"country=" + this.value',
                        update: 'citySelection'
                )}" />
	</div>
	<div id="citySelection">
		<select>
			<option>Select City</option>
		</select>
	</div>
</div>

Notice the use of the remoteFunction in the select tag. The remoteFunction is a nifty grails feature which creates a remote JavaScript function that can be assigned to a DOM event to call the remote method. The exact implementation on the remoteFunction tag depends on the JavaScript library used. In the current example, I will use jQuery. To use jQuery library, add the following line to the main layout file, main.gsp.

<g:javascript library='jquery' />

Reference: http://grails.org/doc/latest/ref/Tags/remoteFunction.html

Our remote function will call the findCityForCountry action in the controller. And the output of the call will updated on the div with id citySelection.

The following is the CountryController.

package com.grails.test

class CountryController {

	//...

	/*
	 * Finding the cities for a specific country
	 */
	def findCityForCountry() {
		def country=Country.get(params.country)
		render(template: 'citySelection', model:  [cities: country.cities])
	}

	/*
	 * For the view
	 */
	def selectChain() {
		[countries : Country.list()]
	}
}

The action, findCityForCountry finds all the cities which belong to that country and renders a template so that the returned cities can be displayed as another drop down.

<!-- This template renders a drop down after a country is selected -->

<g:select name="city.id" from="${cities}" optionValue="name"
          optionKey="id"/> 

This template will replace the div with the specific id whenever the onchange event is fired from the first drop down.

PS: I Love Grails!

Advertisements