Friday, 28 November 2008

grails: toggling Form elements

Occasionally the need arises to have form elements that can be displayed/hidden depending upon a selection made from a drop down list. In this post I demonstrate how this can be achieved using grails + jQuery. The example shown below was developed using grails.1.03 and jQuery-1.2.6

The demonstration is based upon a simplified example of a bug tracker I have been developing. In this example, the domain class, Issue, has 4 fields,
  • name
  • description
  • outcome [e.g. FIXED, NOT FIXED, DUPLICATE]
When a user is editing an issue, only the first three fields should be displayed by default.

It makes sense to display the outcome field only if an issue has been marked "CLOSED" as below. Changing the status to any other value will reset the form and the outcome field will be hidden.

Now to move onto the implementation details.

Step 1: Download & Import jQuery library

In this step you need to download jQuery and place it in the web-app/js folder.
Next you need to import it into the gsp page by placing the following statement between the <head> tags, e.g.

<g:javascript library="jquery-1.2.6.min"/>

Step 2: Create Basic Form Elements

Staying with the gsp page, we create a form and embed within it 2 key elements. The first of these is a drop-down list for the status field. The second element is a stub represented here as an empty table row. Depending on selections to the status field, the stub will either remain empty or be replaced by an extra form element, e.g. a drop-down list for the outcome field.

<td valign="top" class="name">
<label for="status">Status:</label>

<td valign="top" class="value ${hasErrors(bean:issue,field:'status','errors')}">
<g:select id="status" name="status" from="${issue.constraints.status.inList}"
value="${issue.status}" ></g:select>


<tr class="prop" id="selectOutcome"></tr>

Step 3: Code up Ajax call

Next we need to bind the status drop-down list with javascript functions that will detect user selection and transmit its value to a grails controller via Ajax. The .change() and .ajax() functions provided by jQuery fit the bill. They will send the value selected from the status list to the controller and replace the empty stub with any html content sent back.

    $(document).ready(function() {

$("#status").change(function() {
url: "/myform/issue/selectOutcome",
data: "status=" + this.value,
cache: false,
success: function(html) {$("#selectOutcome").html(html);}



The first argument to the ajax function is the url to the grails action. This is of the form /web context/controller/action. The 'data' parameter is the value of the selection made by the user, and the success parameter will replace the empty stub with the response generated by the grails controller.

Step 4: Implement grails action code

* Enable user to select outcome when issue is closed
    def selectOutcome = {

String status = params.status

if(status == "CLOSED")
render (template:"selectOutcome")

render ""


This action defined within an appropriate controller, simply checks to see if the selection made by the user for the status field is set to "CLOSED". If true, it will render the content placed within the template file defined below.

Step 5: create template

Create a template file which should contain the form element you want to display/hide in your target gsp page. Note that this file should be named with a leading underscore, e.g. _selectOutcome.gsp

<td valign="top" class="name">
<label for="outcome">Outcome:</label>

<td valign="top" class="value ${hasErrors(bean:issue,field:'outcome','errors')}">
<g:select id="selectOutcome" name="outcome"
from="${Issue.constraints.outcome.inList}" value=""></g:select>

This will render a label and a select element for the outcome field.

That's it !

You can download the code for this example [] here.

Following the steps above should enable you to implement a similar form toggling feature for your application. Bear in mind though, that there is a potential problem if you will be using jQuery in conjunction with other javascript libraries. However this is a post for another day.

No comments: