Thursday 18 December 2008

grails + ajax: chained selects

From time to time, I run into the following problem. A User has to fill in a form containing 2 drop down lists, and I need to populate the second one with options that depend on the selection made on the first select box. This problem, commonly known as 'Chained Selects' can be easily solved using a dusting of ajax magic.

The principle behind the solution is reasonably straightforward and involves attaching a listener to an HTML element such as a select field. This listener detects events triggered when a User moves the mouse, clicks on an element, etc. In our case, we're interested in the event when the User makes a selection. The listener will then fire a javascript function to perform an ajax call to the server. This call will inform the server as to the selection made on the first select element. Based on this information, the Server will create an HTML snippet containing the second select element and populate it with the correct set of options. This snippet is then sent back as a response to the ajax function, which updates the page accordingly.

To demonstrate this feature, I'm using a simple example containing 2 domain classes, Family and Member. These are listed below,


class Family{

String surname

String description

static hasMany = [members : Member]

static constraints = {
surname(blank:false)
description(blank:false)
members(nullable:true)
}


String toString()
{
return surname
}

}



and,


class Member{
 
String firstName

String lastName

String username

static belongsTo = Family

Family family

static constraints = {
firstName(blank:false)
lastName(blank:false)
username(blank:false)
family(nullable:true)
}

String toString()
{
return lastName + ", " + firstName
}
}




The example revolves around 2 drop down lists. The first of these lists families, e.g. Simpsons and Griffins [Family Guy]. Now I want the second drop down list to be correctly populated with the family members depending on the selection made to the first drop down list.

Step 1: Import 'prototype'

We're going to use prototype to implement this feature. Note you're not restricted to using this library. Others such as jQuery will do an equally impressive job. So the first step is to import in the library as follows,

<g:javascript library="prototype"/>





Step 2: Implement 2 drop down lists

Next we need to implement the 2 select elements as so,

<td>
<label for="family">Family:</label>
</td>
<td>          
<g:select id="familySelect" from="${Family.list().surname}" name="familySelected" value="">
</g:select>
</td>

<td valign="top" class="name">
<label for="member">Member:</label>
</td>


<td>
<div id="memberSelect">
<g:select from="${Member.list()}" name="memberSelected" value="">
</g:select>
</div>
</td>





Step 3: Hook up the ajax code

<g:javascript>
document.observe('dom:loaded', function() {
$("familySelect").observe("change", respondToSelect);
});


function respondToSelect(event)
{
new Ajax.Updater("memberSelect",
"/chainedSelect/family/updateSelect",
{method:'get', parameters: {selectedValue : $F("familySelect")} }
);
}

</g:javascript>



Here we do a couple of things; first of all we attach a listener to the 'familySelect' element to detect for any 'change' events fired when a User makes a selection on this element. Secondly we implement the ajax function that will be triggered when such an event is detected. In this instance we're using prototype's Ajax.Updater() function, which takes a number of arguments that are fully documented elsewhere. The first argument is the id of the element that will be updated by the response to the ajax function. In our case this will be a second drop down list. The second argument is a url pointing to a grails action to service our ajax request. Finally we have a set of optional parameters, the important one being 'selectedValue' which will hold the selection made by the User on the first drop down list.

Step 4: Write the grails action


def updateSelect = {
     def familySelected = Family.find("from Family as family where family.surname=:surname", [surname:params.selectedValue])

render (template:"selectMember", model : ['familySelected' : familySelected])

}




This the grails action that will service the ajax call. Quite simple really. It takes the value selected from the Family dropdown list and uses it to retrieve the corresponding Domain object. This object is then supplied to a tempate file that is subsequently returned as a response to the ajax function in step 3.

Step 5: Write the HTML to update the page.

<g:select
      from="${familySelected.members}"
name="memberSelected" value="">
</g:select>



This is the HTML that will be used to update the page by the ajax function defined above. Note that since I'm using templates, I need to save this in a file whose name should be prefixed with an underscore, '_'.


And here is the final result.
When we select 'Simpsons' as the Family, the second drop down is correctly populated with the members of the Simpson family.



Conversely, if we select 'Griffin' as the family, the second drop down list is populated with the members of the Griffin family.

As a final note, this example could be improved by displaying a default set of values for both selects, when the page is first loaded.

You can download the code [chainSelectFamily.zip] from here.

Monday 15 December 2008

grails: Implementing Search [2]

In a previous post, I described a quick start approach to using the Searchable Plugin. This approach relied on the controller and view supplied with the plugin and is useful for prototyping and testing. However it's not the ideal way to implement search using the plugin.

In this post, I demonstrate how you can customise the Searchable plugin by developing your own controller and views.

The Searchable plugin provides us with 2 distinct ways to search the properties of domain classes. The first of these is the dynamic 'search' method

DomainClass.search(String query)



which is attached to Domain classes that have the static property 'Searchable' defined within them. This restricts the search to only the properties of the Domain Class.

Secondly the plugin provides the SearchableService class,

searchableService.search(String query)



which enables us to search properties across a set of Domain classes. The documentation here provides further details for both approaches.

Step 1-3: Setup

Repeat Steps 1,2 & 3 from the previous post.

I'm using a different example for this post to better demonstrate the similarities and differences between the 2 search mechanisms. This example consists of 2 domain classes listed below and whose purpose is self-explanatory,


class Film{
    String actor

String title

String releaseDate

static Searchable = true

static constraints = {
actor()
title()
releaseDate()
}

}




and,


class Music{
    String artist

String title

String releaseDate


static Searchable = true

static constraints = {
artist()
title()
releaseDate()
}


}



I've implemented step 3 slightly differently. Instead of 1 text-field, we now have the following,

<g:form url='[controller: "customSearch", action: "searchMusic"]' id="searchMusic" name="searchMusic" method="get">
<g:textField name="query" value="${params.query}" size="10"/> <input type="submit" value="Search Music" />
</g:form>

<g:form url='[controller: "customSearch", action: "searchFilm"]' id="searchFilm" name="searchFilm" method="get">
<g:textField name="query" value="${params.query}" size="10"/> <input type="submit" value="Search Film" />
</g:form>


<g:form url='[controller: "customSearch", action: "searchAll"]' id="searchAll" name="searchAll" method="get">
<g:textField name="query" value="${params.query}" size="10"/> <input type="submit" value="Search All" />
</g:form>


Which look like,



These text-fields, allow the User to search individually for Music or Film or indeed collectively across both categories.

Step 4: Implement the Controller

Next, we build our custom search controller, containing actions that will service the text-fields defined in the forms above.


import org.compass.core.engine.SearchEngineQueryParseException

class CustomSearchController{

def searchableService

def searchMusic = {

if(params.query?.trim())
{

def searchResults = Music.search(params.query)

def total = searchResults.total

def list = searchResults.results

return [totalResults: total, list:list, type:"music"]
}
else
{
return [:]
}

}


def searchFilm = {

if(params.query?.trim())
{

def searchResults = Film.search(params.query)

def total = searchResults.total

def list = searchResults.results

return [totalResults:total, list:list, type:"film"]
}
else
{
return [:]
}

}


def searchAll = {

if(!params.max) params.max = 5
if(!params.offset) params.offset = 0

if(params.query?.trim())
{
try
{

def searchResult = searchableService.search(params.query, params)

def total = searchResult.total

def list = searchResult.results

return [totalResults:total, list:list, type:"all"]
}
catch (SearchEngineQueryParseException ex)
{
return [parseException: true]
}
}
else
{
return [:]
}

}

}



At the top of the code, we inject in the plugins' SearchService class. Next we declare the actions required to service the search textfields defined in step 3. The first 2 actions implement search using the dynamic method, whereas the 'searchAll' action uses the SearchableService class mechanism. Both approaches return a 'searchResult' object which contains a number of results data documented here. For our purposes, we're only interested in the search results and their count.

I'm not going to list the view pages here as they're typical gsp pages and don't add any value to the current discussion. However if you're interested, the full code is available [compass2.zip] for download here.

The following images show the results from performing individual and collective searches.

This one shows a 'Music' only search, when I enter a value of 'Will Smith' into the 'search Music' textfield.




This one shows a 'Film' only search, when I enter a value of 'Will Smith' into the 'search Film' textfield.



And finally a collective search, where I enter the value 'Will Smith' into the 'searchAll' text-field.



As expected the plugin returns Music and Films associated with the artist/actor, Will Smith.

The complete code for this example [compass2.zip] is available here.

ps - I noticed something peculiar with Searchable plugin, when using the test data that I generate in the bootstrap class. If I perform a query on 'Will', it returns nothing. Yet if I enter a value of 'Will Smith', then the search returns the correct results. However if I repeat this for 'Mark' and 'Mark Wahlberg', I get identical results, i.e. I get back, Music/Film or both depending on the search type.

Friday 12 December 2008

grails: ajax powered validation [Update]

In a previous post, I outlined a method to perform textfield validation using jQuery. However a recent comment by Jan correctly pointed out a simpler method using grails' own ajax tag, <g:remoteField>.

So to implement this feature in a simpler way do the following,

Step 1: Import scriptaculous

<g:javascript library="scriptaculous"/>







Step 2: Code up the textfield and message div


<g:remoteField controller="member" action="validateUsername" paramName="username" name="username"

update="messageDiv" value="${fieldValue(bean:member,field:'username')}">

</g:remoteField>
<div id="messageDiv" style="color:blue"></div>






The message div will be populated with the ajax response, i.e. the uniqueness validation result.

Step 3: Code up the grails action


def validateUsername = {
        def message = ""

def inUse = ["homer", "marge", "bart", "maggie", "lisa"]

if (params.username)
{
if(inUse.contains(params.username))
{
message = "The username has already been taken. Please enter a different one"
}
else
{
message = "Username Ok !...."
}
}

render message

}




Not much change here other than the response messages.

And that's it.

Thursday 11 December 2008

grails: ajax powered validation

[The contents of this post have been superseded by this post]

I'm not keen on filling in forms, especially ones which are badly designed. To give you an idea of what I'm talking about, consider the typical registration form as shown.

Here the User is expected to provide many personal details in addition to a username which must be unique. The catch is that the user won't know whether the value they have entered is unique until the form has been submitted.

If the username is already in use, the form is redisplayed and the User is asked to provide an alternative.

Now one could envisage situations where the User
has many attempts at providing a valid username until they are successful. This would result in a number of redundant form submissions, page refreshes and clearly a frustrating experience for the User.

One way to address this shortcoming, is to use ajax to perform a validation check. Specifically, once a User has entered a value into the textfield, an ajax call is made to the server requesting that the textfield content be validated. The server performs the check and returns a response which will be used to generate a message informing the User about the validity of the data they have just entered. This approach overcomes the need to perform pointless form submissions and page regeneration.

To demonstrate how to implement this, I've developed a contrived example using jQuery's excellent support for ajax functionality.

Step 1: Download & Import jQuery

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: Setup the Textfield for validation.

Next we need to set up the textfield for validation. In this example I'm going to validate usernames.

<tr class="prop">
     <td valign="top" class="name">
<label for="username">Create Username:</label>
</td>

<td valign="top" class="value ${hasErrors(bean:member,field:'username','errors')}">
<input type="text" id="username" name="username" value="${fieldValue(bean:member,field:'username')}"/>
<div id="usernameMessageOk" style="color:green">Username Ok !....</div>
<div id="usernameMessageFail" style="color:red">Please try again... That username is already in use</div>
</td>

</tr>




Nothing difficult here. The only modifications I've made to the code generated by scaffolding is to add 2 extra divs which will be used to inform the User about the success/failure of the validation. Note these divs are hidden by default as shown in the next section.

Step 3: Hook up the ajax call

In this step we set up jQuery's ajax function to perform the validation


<g:javascript>
    var $j = jQuery.noConflict();

$j(document).ready( function()
{
$j('#usernameMessageOk').hide()
$j("#usernameMessageFail").hide()

$j("#username").change(function()
{
var name=this.value

$j.ajax({
url: "/ajax_validation/member/validateUsername",
dataType: "text",
data: "username=" + this.value,
cache: false,
success:function(text)
{
if(text == 'ok')
{
$j('#usernameMessageOk').show();
$j("#usernameMessageFail").hide()
$j("#username").css({'background-color': 'yellow', 'color':'black' });
}
else
{
$j("#usernameMessageFail").show();
$j('#usernameMessageOk').hide()
$j("#username").css({'background-color': 'red', 'color':'white' , 'border':'solid 1px #0000ff'});
}
}

});

});

});
</g:javascript>








There's quite a few things going on in this snippet. Firstly we hide the message div's using jQuery. Next we bind the username text-field to jQuery's .ajax() function. This will fire whenever the User moves focus away from the textfield e.g. by clicking elsewhere or hitting the 'tab' key.

The ajax function makes a call to a grails action, 'validateUsername' which determines whether the username is in use or not. The Server's response will be used to determine which of the messages contained within the hidden div's are displayed.

Step 4: Code up the grails action to validate the Text-field

In this step we implement the grails action which will receive the text-field content and validate it accordingly. For the purposes of this example I have used a simple list. You can easily replace this with code to inspect the database using GORM.


def validateUsername = {
       def message = "fail"

def inUse = ["homer", "marge", "bart", "maggie", "lisa"]

if (params.username)
{
if(inUse.contains(params.username))
{
message = "fail"
}
else
{
message = "ok"
}
}

render message

}





Quite simple really. The action will perform the uniqueness validation and return the result to the client.

The two possible outcomes are shown below,




and,



That's almost it. One problem remains and that is if the User decides to hit the submit button when the username field has an invalid entry. To solve this simply alter your 'save' action as follows,


def save = {
            def member = new Member(params)

def inUse = ["homer", "marge", "bart", "maggie", "lisa"]

//check that username is still available

if(inUse.contains(member.username))
{
member.errors.rejectValue('username', 'member.username.validator.invalid')
render(view:'create',model:[member:member])
}


if(!member.hasErrors() && member.save())
{
flash.message = "Member ${member.id} created"
redirect(action:show,id:member.id)
}
else
{
render(view:'create',model:[member:member])
}
}





where 'member.username.validator.invalid' is a custom error message defined in the message.properties file. The code above will perform another validity check and ensure that in the case of failure, the User is redirected to the form and our custom error is displayed.

Finally this code could be improved by providing suggestions of usernames to the User.

Friday 5 December 2008

grails: Implementing Search

The ability for a User to perform searches for entries in the database is a critical requirement for most web apps. Implementing such a feature from scratch can be a rather daunting task. Thankfully there are open source solutions that alleviate the need to build your own search engine. Two of these, Lucene and Compass are bundled into a grails plug-in, enabling you to provide a powerful and flexible search capability for your Users.

Implementing search using this plugin is actually quite simple, as I will demonstrate in this post.

Step 1: Download & Install Searchable plugin

The first step is to download and install the Searchable plugin. Navigate to your project folder and issue the following command from a console window.

grails install-plugin searchable
 



Please note this assumes you are using JDK1.5+. If however, you're still using JDK1.4, then invoke the above command ensuring that the term 'searchable' is replaced with 'searchable14'.

Step 2: Modify Domain class

Open up your Domain class and add the statement 'static searchable = true '. In the example below, my domain class is called Issue and is a simplification of an actual class that I'm developing for a pet project. This flag enables the plugin to identify which records are available for search.

class Issue{

String title
String description
String status
String priority

static searchable = true

static constraints = {
title()
description()
status(inList : ["UNASSIGNED", "ASSIGNED", "CLOSED"])
priority(inList: ["TRIVIAL", "MINOR", "MAJOR", "CRITICAL", "BLOCKER"])
}

String toString(){
return title
}
}




Step 3: Create Search Textfield.

The next step is to provide a search box into which the User can enter search parameters. I do this by creating a search textfield like the one below,


<g:form url='[controller: "searchable", action: "index"]' id="searchableForm" name="searchableForm" method="get">
      <g:textField name="q" value="${params.q}" size="15"/> <input type="submit" value="Search" />

</g:form>




The textfield is embedded within a form that will pass its contents to an 'index' action on the plugins 'search' controller, either when the User clicks on the 'search' button or hits the 'enter' key.

To show what this would look like, I've knocked up a simple web app using the Issue class above. We now have something that looks like this

You can clearly see the search box at the top of the navigation bar. I've used the bootstrap class to create some test data which are shown in the table. This data will enable me to demonstrate that the search capability works and returns the expected results.

Entering a value of "CLOSED" into the search box returns the following response,

Although the plugin has successfully returned the expected results, the presentation of the data leaves a lot to be desired. Our application no longer looks consistent. Every time a User performs a search they'll end up with a page that clearly doesn't look right and they'll have to click on the browser back button to navigate somewhere else. The next step should fix this.

Step 4: Edit View for Search Results

Create a new folder 'searchable' under grails-app/views.

In this folder we want to place a copy of the view used by the plugin to display the results. The copy will subsequently be modified to generate a view that is consistent with our web application. So copy the file web-app/plugins/searchable-0.5.1/grails-app/views/searchable/index.gsp into the newly created folder. Here /web-app/ is the name of your project and searchable-0.5.1 refers to the version of the plugin that is in use. For my case, this happens to be 0.5.1. Check the version that you have downloaded and adjust accordingly.

Next we modify index.gsp in the following way,
  1. Delete all the css styling defined within the <head> tags
  2. Place the following meta-tag between the <head> tags
<meta name="layout" content="main"/>



Following these two steps should be sufficient to generate a results view that is consistent with your application. However for my application, I had to perform an extra step and rename the id's for the <header> and <main> divs. This was to prevent a conflict with css styling from my own style sheet.

The end result now looks like



This completes the tutorial, which I hope you will find useful. My intention was to enable you to get started quickly and in that respect the post has not and could not cover the full power of this fantastic plugin. That may be a task for another day.

The example code demonstrated here is available for download [compass.zip] from here. Please note that this is missing the plugin. I had to delete that in order to comply with the File Upload limit. However this shouldn't be a problem if you follow the installation instructions given above.

Tuesday 2 December 2008

grails: Templates


Previously I've used Tiles to provide a common look and feel to web applications.

Template frameworks like Tiles achieve this effect by enabling developers to define page fragments which are then assembled into a complete page at run time. This approach is quite appealing since the fragments are by nature reusable view components, thereby enhancing maintainability and reducing development time.

Grails uses 'SiteMesh' to perform this function, and does so in a simple and flexible way. No XML configuration here !!!

A typical page layout is displayed in the figure on the left. This is made up from the following view components. The topbar will display time/date and a welcome message identifying the user, and potentially a 'logout' button. The header will showcase the name of the application, and the navbar will contain a menu for navigation. The central panel will contain the content to be displayed to the user. Finally the footer will hold information such as Terms & Conditions, a Copyright notice etc.

To implement the layout shown here execute the following steps,

Step 1: start
You need a project, ideally populated with at least a single domain class with it's associated controllers and views. The picture below shows the default layout generated by grails.



Step 2: create re-usable templates
With the exception of the header and content fragments, we start by creating the templates for the remaining view elements. Create a folder called 'common' under the views/ folder. In this folder create the following files
  • _topbar.gsp
  • _navbar.gsp
  • _footer.gsp
Note that these files should start with an underscore, '_' and should not contain any HTML tags such as <head>, <body>.

Step 3: populate _topbar.gsp

<div id="welcome">
12th June 2008
</div>

<div id="menu">
Welcome Ned Flanders | <a href="#">logout</a>
</div>




Step 4: populate _navbar.gsp

<div class='navbarBox'>

<div class="navcontainer1">
<h1 class="panelHeader">
Users
</h1>
<ul class="navlist">
<li>
<g:link controller="user" action="list">
List Users
</g:link>
</li>
<li>
<g:link controller="user" action="create">
Add User
</g:link>
</li>
<li>
<g:link controller="user" action="edit">
Edit User
</g:link>
</li>
<li style="color:grey"><a href="#">Remove User</a></li>
</ul>
</div>

</div>

<div class="navbarBox">

<div class="navcontainer1">
<h1 class="panelHeader">
Actions
</h1>
<ul class="navlist">
<li><a href="#">do something 1</a></li>
<li><a href="#">do something 2</a></li>
<li><a href="#">do something 3</a></li>
<li><a href="#">do something 4</a></li>
<li><a href="#">do something 5</a></li>
</ul>
</div>

</div>

<div class='navbarBox'>

<div class="navcontainer1">
<h1 class="panelHeader">
Leisure
</h1>
<ul class="navlist">
<li><a href="#">Activity 1</a></li>
<li><a href="#">Activity 2</a></li>
<li><a href="#">Activity 3</a></li>
<li><a href="#">Activity 4</a></li>
<li><a href="#">Activity 5</a></li>
</ul>
</div>

</div>




Step 5: populate _footer.gsp

<span class="copyright"> © 2008</span>
<span class="tandc"> <a href="#">Terms & Conditions </a></span>



Step 6: modify main.gsp

Once the reusable view components have been defined, we include them into the layout. Open the file /views/layout/main.gsp and replace all the code between the <body> tags with the following lines of code

<div id="page">

<div id="topbar">
<g:render template="/common/topbar" />
</div>

<div id='header'>
<h1 style="font-size:62px;text-align:center;"> Moongrails </h1>
</div>

<div id="content">
<g:layoutBody />
</div>

<div id='navbar'>
<g:render template="/common/navbar" />
</div>

<div id="footer">
<g:render template="/common/footer" />
</div>

</div>



The content in the central fragment will be populated by the gsp pages in the various view folders. When the user navigates to any particular page, e.g. /user/list.gsp, grails imports in the contents of that page and inserts it into the content div via the <g:layoutBody /> tag.

Step 7: modify main.css

The final step is to code up the styling for the various templates.

/* --------------------------------------*/
/* PAGE container */
/* --------------------------------------*/
/* Contains page contents e.g header,navbar, content, footer */
#page {
width: 90%;
margin: 0px auto;
padding: 4px 0;
text-align:left;
}


/* --------------------------------------*/
/* TOPBAR */
/* --------------------------------------*/
#topbar {
text-align:left;
padding:5px 0;
border-top: 1px solid #333;
background: #aaaaaa;
height: 15px;
}

#topbar #welcome{
float:left;
text-align:left;
font-size: 12px;
padding: 0px 0px 0px 10px;
color: #ffffff;
}

#topbar #menu{
float: right;
text-align: right;
font-size: 12px;
color: #ffffff;
}

#topbar #menu a{
color: #ffffff;
padding: 0px 10px 0px 0px;
}

/* --------------------------------------*/
/* HEADER */
/* --------------------------------------*/
#header {
background: #227ce8;
height: 150px;
margin: 0px auto;
border-top: solid 2px #000000;
}

#header h1 {
font-family:Arial,sans-serif;
color: white;
padding: 20px 0 0 6px;
font-size:1.6em;
}

/* --------------------------------------*/
/* CONTENT */
/* --------------------------------------*/
#content {
float: left;
width: 80%;
color: #000;
margin: 0 0.5em 0 0;
padding: 0px 10px 5px 0px;
}

/* --------------------------------------*/
/* NAVBAR */
/* --------------------------------------*/
#navbar {
float: right;
width: 210px;
color: #ff0000;
padding: 0px 5px 0px 5px;
margin: 0 0 0 0;
border-left: 1px solid #d3d3d3;
border-right: 1px solid #d3d3d3;
}


/*2.12.08 IE correction to drop sidbarBox*/
html>body #navbar .navbarBox {

width: 200px;
color: #0000ff;
background: #2175bc;
margin: 0px 5px 5px 5px ; /*ensure there's a gap between text in navbar and start of navbar*/
}

/*Header for boxes in navbar menu*/
#navbar .navbarBox h1{
background: #227ce8;
color: #ffffff;
font-weight: normal;
font-size: 16px;
}

/* --------------------------------------*/
/* FOOTER */
/* --------------------------------------*/
#footer {
border: solid 1px #000000;
height: 20px;
clear:both;
padding:5px;
margin: 5px 5px 5px 5px;
background: #ababab url(../images/footer.png) repeat;
}

#footer .tandc{
float: right;
color: #0000ff;
}

#footer .copyright{
float: left;
color: #0000ff;
}

/*----------------------------------------------------------*/
/* Improved vertical menu for navbar: see http://css.maxdesign.com.au/listamatic/vertical10.htm
/*----------------------------------------------------------*/

h1.panelHeader{
font-weight: bold;
text-align: center;
}

/*need html>body hack otw menu boxes, e.g. navbarbx all merge together*/
html>body .navcontainer1
{
width: 100%;
border-right: 1px solid #000;
font-family: Verdana, Lucida, Geneva, Helvetica, Arial, sans-serif;
background-color: #227ee8;
color: #333;
}

.navcontainer1 ul
{
list-style: none;
margin: 0;
padding: 0;
border-top: 2px solid #000000;
}

.navcontainer1 li
{
border-bottom: 1px solid #90bade;
margin: 0;
}

.navcontainer1 li a
{
display: block;
padding: 5px 5px 5px 0.5em;
border-left: 10px solid #1958b7;
border-right: 10px solid #508fc4;
background-color: #2175bc;
color: #ffffff;
text-decoration: none;
}

html>body .navcontainer1 li a { width: auto; }

.navcontainer1 li a:hover
{
border-left: 10px solid #1c64d1;
border-right: 10px solid #5ba3e0;
background-color: #2586d7;
color: #fff;
}





The menu in the navbar was styled using resources from Listomatic's Menu Tutorial. This is an excellent site which provides hints on how to create and style vertical/horizontal css menus.

The final result is displayed below.



The application suffers from at least a couple of problems when viewed using IE6. One of these shows the menu items in the navbar displayed in gray even though the css marks them to be white. To resolve this simply remove 'a:visited' from the line

a:link, a:visited, a:hover {



in the file main.css.

This example was developed using grails 1.03 and can be downloaded [templates.zip] from here.

Monday 1 December 2008

grails: jQuery '$' conflicts

I'm a keen user of jQuery, which I've found to be a very robust, reliable and fairly intuitive javascript library. It offers tonnes of documentation in addition to a vast library of plugins to cater for most if not all your needs.

My experience with this library has been primarily problem-free except for one occasion when I attempted to use it in conjunction with another javascript library. Specifically, I discovered that previously working code implementing jQuery ceased to function when I imported in the scriptaculous library.

Generally jQuery has been well designed to circumvent potential collisions with other libraries through the use of a unique namespace. In spite of this, problems do occur, because of the role of the '$' function in jQuery and other competing libraries. In jQuery it is a shortcut reference to jQuery but means something different for scriptaculous. The solution to this problem is actually quite straightforward and is documented here.

Consider the following code [described in post: grails: toggling Form elements, step 3]



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







This code allows us to use ajax to display/hide form elements based upon the selection made by a user from a drop down list. The code works fine, until I import in scriptaculous


<g:javascript library="scriptaculous"/>



Following this step, I discovered that the previous code stopped working. Implementing the suggestions provided by the jQuery website, I modified the above code as follows



<g:javascript>
    var $j = jQuery.noConflict();

$j(document).ready(function() {

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

});

</g:javascript>




Looking carefully at the before and after code snippets, you can see that the main difference has been to replace '$' with the variable '$j' where j is simply an alias for the function jQuery.noConflict(). This function tells jQuery to ignore the $ function so that scriptaculous or some other library can use it. Modifying the code in this way enables both the scriptaculous and jQuery code to function as expected. QED.