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.

3 comments:

jan said...

Wouldn't be easier to achieve the same functionality with the remoteField tag that is part of Grails?

mo said...

Yes you're absolutely right. It's simpler to use the <g:remoteField> tag.I'm currently working on a few things that may not be possible using the standard tag set and I was exploring how to use a combination of grails + jQuery as a work round. I was hoping to report on these to showcase how marriage between 2 fantastic technologies can enhance a standard web application. This one must have fallen through the net. I'll post the simpler solution. Thanks.

Raul said...

Hi, please take a look to the Grails ValidationField Plugin, this plugin make this much more easier:

http://www.grails.org/plugin/validation-field