Friday, 7 March 2008

Maxlength HTML attribute for domain objects

With Grails domain classes, constraints can be specified - including the maximum size of a String. For example:

class Book {
String title

static def constraints = {
title(maxSize:20)
}
}

When using 'grails generate-all' to create scaffolding, you'll notice that create.gsp and add.gsp hardcode the maxlength attribute:
<input type="text" maxlength="20" id="title" name="title"
value="${fieldValue(bean:book,field:'title')}"/>

It is possible to directly reference the constraint, meaning that the maxlength attribute is not hardcoded - and thus only defined once (in the domain object):
<input type="text" maxlength="${Book.constraints.title.getMaxSize()}"
id="title" name="title"
value="${fieldValue(bean:book,field:'title')}"/>

In fact, we can go even further and create a tag where we pass in the object itself, and the name (which matches the property name) and we can dynamically generate the maxlength and value attributes.

So when editing a book with the title set to 'my new book', the following code:
<jttext:textField object="${book}" name="title"/>

produces the following HTML:
<input type="text" name="title" maxlength="20" value="my new book"
id="title" />

The tag library to produce this is as follows:

import org.codehaus.groovy.grails.plugins.web.taglib.FormTagLib

class TextTagLib extends FormTagLib {
static namespace = "jttext"

def textField = {attrs ->
attrs.type = "text"
attrs.tagName = "textField"

def object = attrs.remove('object')
def maxlength = object.constraints."${attrs.name}".getMaxSize()
if(maxlength) {
attrs.put('maxlength', maxlength)
}
attrs.put('value', fieldValue(bean:object,field:attrs.name))

def result = field(attrs)
if (result) {
out << result
}
}
}

It makes sense to me that the tag does all of the work - it reduces duplication and makes the code more readable.

What do you think?