Thursday, September 16, 2010

Validation in Grails

I had a long time inertia against writing exception handling and displaying user friendly message in the UI. And it appears I am not alone! But is time to shake it off. Welcome to Validation and Error messaging - Grails Style!
Before I get started (and promise I will be done before I notice) some facts.

1. You define constraints in your Domain class. For example..
package foo.bar;
class Item {
String itemName
..
static constraints = {
itemName(blank:false,unique:true, nullable:false)
..
}

2. Now if you say..
def someCoreItemInstance = new Item()
Domain classes have implicit object "error" in them. In fact the above line of code will already populate the error object since someCoreItemInstance violates the constraint (nullable:false). However in an explicit call someCoreItemInstance.validate() will create "error" object and put it in the someCoreItemInstance object; but

3. someCoreItemInstance.save() will automatically call someCoreItemInstance.validate()


In the controller even if you do not do anything extra (there are rare exceptions like while doing a transaction) you must be calling the save method. That automatically puts the error object inside someCoreItemInstance object; but "how do I create nice user friendly error message"?

This is where convention over configuration (over coding!) comes into play. Your error object by default looks for a message in /i18/messages.properties file in the following format.
[Class class] [Attribute attribute] [Constraint constraint]

So in the above example all you need to create nice error message is a message like this...
foo.bar.Item.itemName.nullable = Hey please enter item name

All that remains is to display the message in the gsp page; for that...
<g:hasErrors bean="${someCoreItemInstance}">
<div class="errors">
<g:renderErrors bean="${someCoreItemInstance}" as="list" />
</div>
</g:hasErrors>
...

And thats it!
So all you really need is the message in the correct format and the gsp code to display.

But I want to validate non-domain (where domain class is a class representing a persisted object, think hibernate) classes too. How do I inject the 'error' object into them? Well there seems to be a bit of a confusion but here is what always works.

Lets say you have non-domain class ItemBean and you want to be able to call validate() and want the rest of the magic to happen. So you need...
package foo.bar;
@Validatable
class ItemBean {
...
}

and also enter the following line in Config.groovy
grails.validateable.classes = [foo.bar.ItemBean] // more comma separated allowed


Happy Validating!