I linger on several mailing lists and forums looking to help folks with their Grails issues and one of the more common questions I see is about using the Grails select taglib along with remoteFunction.
I decided to create a sample project with a couple of different ways to select an option from a select element that uses ajax to update a 2nd select element, since this is what most questions I see are asking about. And I’ll use this blog post to walk through the code and hopefully I’ll be able to just post to this example when answering questions in the future.
I’m using Grails 2.2.0 which uses the jQuery plugin by default and I’ve also installed the grails bootstrap plugin. Generally, I’ll pull down bootstrap’s files and include them via the resource plugin myself but for this example it was faster to do it this way.
The first thing I did was clean up the main.gsp layout and remove a lot of the Grails specific stuff from executing create-app. I also added the jquery library and the bootstrap module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
1 2 3 4 5 6
main.js only includes a single function that handles rendering JSON data into option elements and appending them to a select element. We’ll use this function from the remoteFunction call in the gsp.
1 2 3 4 5 6 7 8 9
Next, I wanted to setup the controller for the example and I needed a good parent-child relationship but didn’t want to mess with creating Domains so I created the SelectController and put some simple static maps of data to use which looks like this:
We have some states and their cities. The idea is that we will select a state and then another select will populate with its cities.
The first example is going to populate a select with cities from JSON. I created an index.gsp with the following code:
The key parts are:
- Line 3 gets the initial map of states to populate the select list.
- Line 7 defines the remoteFunction which will call the citiesByState action. I’ll explain the .json in a bit
- Line 8 specifies the function to call when the response is successful and passes in the data from the response to that function. In our case, JSON data.
- Line 9 defines the params that get passed through the request.
- this.value is the value of the state select
The SelectController gets updated with the following code:
- Line 9 pushes the state map to the index view.
- Line 13 gets cities map based on the state abbreviation from the request
Lines 14-18 pass the cities map back as JSON. I use withFormat here because I want to use the same method for different response types. In a moment I’ll add the HTML response type to the same method. If you run the code as is:
select a state from the select list
- An ajax call is made to citiesByState
- The cities are returned as JSON
- remoteFunction calls populateCities() in main.js
- populateCities adds the options to the disabled cities select and enables it.
The next part of the example will make the same ajax call to the server but instead of using a JSON response and our populateCities() function to fill in the city options, we will render a template that gets returned as HTML and remoteFunction will update an HTML element on the page.
index.gsp now looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
I’ve added another form section so both can execute on the same page. The main difference is on line 56. I tell the remoteFunction that on success, update the container with an ID of citiesContainer with the response.
SelectController now looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
I added an additional block in withFormat for HTML and told it to render the _cities.gsp template passing in the cities map. That template looks like this:
1 2 3 4
The complete project can be accessed on github here: