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
  • status [e.g. UNASSIGNED, ASSIGNED, CLOSED]
  • 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.

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

<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>
</td>

</tr>

<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.


<g:javascript>
    $(document).ready(function() {

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

});

</g:javascript>








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>

<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>
</td>





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

That's it !

You can download the code for this example [myform.zip] 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.

Thursday 27 November 2008

grails: many-to-many Backref exception

I ran into a rather interesting problem recently when attempting to save a many-to-many association between 2 domain objects. I've managed to reproduce the problem in a sanitised test case which I'll present here. The app was developed using grails 1.03.

Consider the domain classes, Project and User defined as follows



and



Note that I've used a List to store the objects on the other side of the many-to-many relationship. Next I create test data in the BootStrap class as follows,




Upon deploying the application, I get the following exception

Caused by: org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value: Project._User_projectsBackref; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: Project._User_projectsBackref

This is rather puzzling. The exception suggests an object referencing issue, but it's not clear what this is. Equally baffling is the solution to this problem. The domain classes above use a List to store their many-to-many collaborators. By simply reverting to the default collection type, i.e. a Set, the problem disappears. So for the code above remove line 7 in both classes and the app should deploy without any problems.

I recently came across this JIRA ticket which suggests this is a problem the grails team are aware of.

Thursday 13 November 2008

grails + ajax: autocomplete

Having an autocomplete feature on a textfield is a good way of improving user experience. As the user types a string, the textfield tries to anticipate what the user is typing and offers possible matches. Clicking on a match results in autopopulation of the textfield thereby saving time for the user.

In this post I describe a way of implementing this feature using grails and scriptaculous. To help explain the method, I will develop a simple application where we have a form containing a text field for countries of the world. As a user begins to enter the name of the country, the textfield presents a list of possible matches. Finding the country he wants, the user clicks on the entry and the textfield is immediately populated with the selection [see picture below].




















Before ploughing into the specific details of how to implement this feature, it's useful to gain a high level view of what is going on "under the hood".


To begin with, the textfield needs to be registered with an ajax function [defined within scriptaculous]. This function will detect keystrokes and then call an appropriate action within a grails controller. The action in turn will employ GORM to perform a hibernate search for records that match the pattern of text the user has just entered. The results from this search will be returned to the view and rendered as a list just under the text box.

So to build your own autocomplete search text field, you need to do the following,

Step 1: Import in scriptaculous

Copy and paste the following into the header element of your gsp page


<g:javascript library='scriptaculous'/>



Step 2: create the form with the text field and the supporting div

Within the gsp page create a text field and a supporting div to display the results of the autocomplete feature.


<g:form name="find"..... >

..............
..............

<%-- Text field supporting autocomplete --%>
<input type="text" id="autocomplete" name="autocomplete_parameter"/>


..........
..........

</g:form>

<%-- supporting div to display potential matches.--%>

<div id="autocomplete_choices" class="autocomplete" ></div>




Step 3: Implement grails action to respond to ajax call

In this section we create the action in a grails controller that will respond to the ajax function.

def ajaxAutoComplete = {

if(params.autocomplete_parameter)
{
def input = params.autocomplete_parameter + '%'

def list = Country.findAll("from Country as country
where lower(country.name) like :name", [name:input])

StringBuffer idList = new StringBuffer()

idList.append("<ul>")

list?.each{c -> idList.append("<li>" + c.name+"</li>")}

idList.append("</ul>")

render idList.toString()
}
}



The action receives a variable called autocomplete_parameter containing the keystrokes entered by the user. This string is used in a hibernate query to retrieve the set of countries with matching names. Finally the search results are embedded within a html list that is subsequently rendered to view.

Step 4:
Code up the ajax call

In this case, we're simply setting up and calling an ajax function [Ajax.Autocompleter] from scriptaculous.


<html>
<head>
<meta equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="layout" content="main">

<g:javascript library="'scriptaculous'/">

<g:javascript>
window.onload = function()
{
new Ajax.Autocompleter("autocomplete",
"autocomplete_choices",
"/autocomplete/country/ajaxAutoComplete",
{}
);
}
</g:javascript>





The first argument to this function is the id of the UI element we're monitoring, i.e. the textfield. Next we pass in the name of the element that will be updated with the results of the ajax call. Thirdly we pass in the url to the grails code that will service the ajax function. This url is of the form /web context/controller/action.

Step 5: Add styling to the supporting div.

Place the following excerpt into /web-app/css/main.css


div.autocomplete {
position:absolute;
width:250px;
background-color:#8cafea;
margin:0px;
padding:0px;
overflow:hidden;
border: 1px solid;
}

div.autocomplete ul {
list-style-type:none;
margin:0px;
padding:0px;
overflow:auto;
}

div.autocomplete ul li.selected { background-color: #294597; color: #ffffff}

div.autocomplete ul li {
list-style-type:none;
display:block;
margin:0 0 0 5;
padding:2px;
height:12px;
cursor:pointer;
}


Presto !!! We now have an autocomplete solution using grails + scriptaculous. This implementation could be improved further by limiting the number of matches found by hibernate to some number say 8. This would prevent situations whereby the user is inundated with a long list of possible matches. There are alternative ways to implement this feature. These include using the RichUI or GrailsUI plugins.

You can download this example [autocomplete.zip] here. This example was built using grails 1.03

Wednesday 12 November 2008

grails: Hello

Hi,

I'm currently in the process of learning grails by working on a pet project [an issue/bug tracker]. It's my intention to report some of what i've learnt on this blog not only for future recall but also hoping that others may find some of it useful.

I selected a bug/issue tracker since it would capture many of the features you would expect to find on a business reporting tool. Furthermore there seems to be a splurge of example applications based on blogging tools. I wanted to do something different.

I plan to document this exercise and release it together with the source code so that others may use it to learn grails. The application is called 'Gravitium' which is a combination of 'Grails' and 'Vitium' [latin for fault or defect].

Typical features for this application include,
  • basic CRUD [using GORM]
  • security [using acegi plugin]
  • search [using the compass plugin]
  • Charts [using google-chart plugin]
  • Reporting [using jasper plugin]
Additionally the application will use JQuery to provide enriched ui experience to supplement the functionality provided by the scriptaculous library which is included with grails.

In future posts, I'll present examples of how to implement some of these features.

Here's a snapshot of the app as it stands.