(Quick Reference)

6.1.6 Data Binding - Reference Documentation

Authors: Graeme Rocher, Peter Ledbrook, Marc Palmer, Jeff Brown, Luke Daley, Burt Beckwith

Version: null

6.1.6 Data Binding

Data binding is the act of "binding" incoming request parameters onto the properties of an object or an entire graph of objects. Data binding should deal with all necessary type conversion since request parameters, which are typically delivered by a form submission, are always strings whilst the properties of a Groovy or Java object may well not be.

Grails uses Spring's underlying data binding capability to perform data binding.

Binding Request Data to the Model

There are two ways to bind request parameters onto the properties of a domain class. The first involves using a domain classes' Map constructor:

def save() {
    def b = new Book(params)
    b.save()
}

The data binding happens within the code new Book(params). By passing the params object to the domain class constructor Grails automatically recognizes that you are trying to bind from request parameters. So if we had an incoming request like:

/book/save?title=The%20Stand&author=Stephen%20King

Then the title and author request parameters would automatically be set on the domain class. You can use the properties property to perform data binding onto an existing instance:

def save() {
    def b = Book.get(params.id)
    b.properties = params
    b.save()
}

This has the same effect as using the implicit constructor.

Data binding and Single-ended Associations

If you have a one-to-one or many-to-one association you can use Grails' data binding capability to update these relationships too. For example if you have an incoming request such as:

/book/save?author.id=20

Grails will automatically detect the .id suffix on the request parameter and look up the Author instance for the given id when doing data binding such as:

def b = new Book(params)

An association property can be set to null by passing the literal String "null". For example:

/book/save?author.id=null

Data Binding and Many-ended Associations

If you have a one-to-many or many-to-many association there are different techniques for data binding depending of the association type.

If you have a Set based association (the default for a hasMany) then the simplest way to populate an association is to send a list of identifiers. For example consider the usage of <g:select> below:

<g:select name="books"
          from="${Book.list()}"
          size="5" multiple="yes" optionKey="id"
          value="${author?.books}" />

This produces a select box that lets you select multiple values. In this case if you submit the form Grails will automatically use the identifiers from the select box to populate the books association.

However, if you have a scenario where you want to update the properties of the associated objects the this technique won't work. Instead you use the subscript operator:

<g:textField name="books[0].title" value="the Stand" />
<g:textField name="books[1].title" value="the Shining" />

However, with Set based association it is critical that you render the mark-up in the same order that you plan to do the update in. This is because a Set has no concept of order, so although we're referring to books0 and books1 it is not guaranteed that the order of the association will be correct on the server side unless you apply some explicit sorting yourself.

This is not a problem if you use List based associations, since a List has a defined order and an index you can refer to. This is also true of Map based associations.

Note also that if the association you are binding to has a size of two and you refer to an element that is outside the size of association:

<g:textField name="books[0].title" value="the Stand" />
<g:textField name="books[1].title" value="the Shining" />
<g:textField name="books[2].title" value="Red Madder" />

Then Grails will automatically create a new instance for you at the defined position. If you "skipped" a few elements in the middle:

<g:textField name="books[0].title" value="the Stand" />
<g:textField name="books[1].title" value="the Shining" />
<g:textField name="books[5].title" value="Red Madder" />

Then Grails will automatically create instances in between. For example in the above case Grails will create 4 additional instances if the association being bound had a size of 2.

You can bind existing instances of the associated type to a List using the same .id syntax as you would use with a single-ended association. For example:

<g:select name="books[0].id" from="${bookList}"
          value="${author?.books[0]?.id}" />

<g:select name="books[1].id" from="${bookList}" value="${author?.books[1]?.id}" />

<g:select name="books[2].id" from="${bookList}" value="${author?.books[2]?.id}" />

Would allow individual entries in the books List to be selected separately.

Entries at particular indexes can be removed in the same way too. For example:

<g:select name="books[0].id"
          from="${Book.list()}"
          value="${author?.books[0]?.id}"
          noSelection="['null': '']"/>

Will render a select box that will remove the association at books0 if the empty option is chosen.

Binding to a Map property works the same way except that the list index in the parameter name is replaced by the map key:

<g:select name="images[cover].id"
          from="${Image.list()}"
          value="${book?.images[cover]?.id}"
          noSelection="['null': '']"/>

This would bind the selected image into the Map property images under a key of "cover".

Data binding with Multiple domain classes

It is possible to bind data to multiple domain objects from the params object.

For example so you have an incoming request to:

/book/save?book.title=The%20Stand&author.name=Stephen%20King

You'll notice the difference with the above request is that each parameter has a prefix such as author. or book. which is used to isolate which parameters belong to which type. Grails' params object is like a multi-dimensional hash and you can index into it to isolate only a subset of the parameters to bind.

def b = new Book(params.book)

Notice how we use the prefix before the first dot of the book.title parameter to isolate only parameters below this level to bind. We could do the same with an Author domain class:

def a = new Author(params.author)

Data Binding and Action Arguments

Controller action arguments are subject to request parameter data binding. There are 2 categories of controller action arguments. The first category is command objects. Complex types are treated as command objects. See the Command Objects section of the user guide for details. The other category is basic object types. Supported types are the 8 primitives, their corresponding type wrappers and java.lang.String. The default behavior is to map request parameters to action arguments by name:

class AccountingController {

// accountNumber will be initialized with the value of params.accountNumber // accountType will be initialized with params.accountType def displayInvoice(String accountNumber, int accountType) { // … } }

For primitive arguments and arguments which are instances of any of the primitive type wrapper classes a type conversion has to be carried out before the request parameter value can be bound to the action argument. The type conversion happens automatically. In a case like the example shown above, the params.accountType request parameter has to be converted to an int. If type conversion fails for any reason, the argument will have its default value per normal Java behavior (null for type wrapper references, false for booleans and zero for numbers) and a corresponding error will be added to the errors property of the defining controller.

/accounting/displayInvoice?accountNumber=B59786&accountType=bogusValue

Since "bogusValue" cannot be converted to type int, the value of accountType will be zero, controller.errors.hasErrors() will be true, controller.errors.errorCount will be equal to 1 and controller.errors.getFieldError('accountType') will contain the corresponding error.

If the argument name does not match the name of the request parameter then the @grails.web.RequestParameter annotation may be applied to an argument to express the name of the request parameter which should be bound to that argument:

import grails.web.RequestParameter

class AccountingController {

// mainAccountNumber will be initialized with the value of params.accountNumber // accountType will be initialized with params.accountType def displayInvoice(@RequestParameter('accountNumber') String mainAccountNumber, int accountType) { // … } }

Data binding and type conversion errors

Sometimes when performing data binding it is not possible to convert a particular String into a particular target type. This results in a type conversion error. Grails will retain type conversion errors inside the errors property of a Grails domain class. For example:

class Book {
    …
    URL publisherURL
}

Here we have a domain class Book that uses the java.net.URL class to represent URLs. Given an incoming request such as:

/book/save?publisherURL=a-bad-url

it is not possible to bind the string a-bad-url to the publisherURL property as a type mismatch error occurs. You can check for these like this:

def b = new Book(params)

if (b.hasErrors()) { println "The value ${b.errors.getFieldError('publisherURL').rejectedValue}" + " is not a valid URL!" }

Although we have not yet covered error codes (for more information see the section on Validation), for type conversion errors you would want a message from the grails-app/i18n/messages.properties file to use for the error. You can use a generic error message handler such as:

typeMismatch.java.net.URL=The field {0} is not a valid URL

Or a more specific one:

typeMismatch.Book.publisherURL=The publisher URL you specified is not a valid URL

Data Binding and Security concerns

When batch updating properties from request parameters you need to be careful not to allow clients to bind malicious data to domain classes and be persisted in the database. You can limit what properties are bound to a given domain class using the subscript operator:

def p = Person.get(1)

p.properties['firstName','lastName'] = params

In this case only the firstName and lastName properties will be bound.

Another way to do this is is to use Command Objects as the target of data binding instead of domain classes. Alternatively there is also the flexible bindData method.

The bindData method allows the same data binding capability, but to arbitrary objects:

def p = new Person()
bindData(p, params)

The bindData method also lets you exclude certain parameters that you don't want updated:

def p = new Person()
bindData(p, params, [exclude: 'dateOfBirth'])

Or include only certain properties:

def p = new Person()
bindData(p, params, [include: ['firstName', 'lastName]])

Note that if an empty List is provided as a value for the include parameter then all fields will be subject to binding if they are not explicitly excluded.