Grails has very good support for binding request parameters to a domain object and it's associations. This largely relies on detecting request parameters that end with .id
and automatically loading those from the database.
However, it's not clear how to populate the associations of a command object. Take the following example:
class ProductCommand {
String name
Collection<AttributeTypeCommand> attributeTypes
ProductTypeCommand productType
}
This object has a single-ended association with ProductTypeCommand
and a many-ended association with AttributeTypeCommand
. The list of all attribute types and product types are available from an implementation of this interface
interface ProductAdminService {
Collection<AttributeTypeCommand> listAttributeTypes();
Collection<ProductTypeCommand> getProductTypes();
}
I use this interface to populate the product and attribute type selection lists in a GSP. I also dependency-inject this interface into the command object, and use it to "simulate" attributeTypes
and productType
properties on the command object
class ProductCommand {
ProductAdminService productAdminService
String name
List<Integer> attributeTypeIds = []
Integer productTypeId
void setProductType(ProductTypeCommand productType) {
this.productTypeId = productType.id
}
ProductTypeCommand getProductType() {
productAdminService.productTypes.find {it.id == productTypeId}
}
Collection<AttributeTypeCommand> getAttributeTypes() {
attributeTypeIds.collect {id ->
productAdminService.getAttributeType(id)
}
}
void setAttributeTypes(Collection<AttributeTypeCommand> attributeTypes) {
this.attributeTypeIds = attributeTypes.collect {it.id}
}
}
What actually happens is that the attributeTypeIds
and productTypeId
properties are bound to the relevant request parameters and the getters/setters "simulate" productType
and attributeTypes
properties. Is there a simpler way to populate the associations of a command object?
What I've seen in some projects was the use of the Lazy* collection classes from Apache Commons Collections. It used code like this to lazily initialize a command association:
class ProductCommand {
String name
String type
List<AttributeTypeCommand> attributes = org.apache.commons.collections.list.LazyList.decorate(new ArrayList(), new org.apache.commons.collections.functors.InstantiateFactory(AttributeTypeCommand.class))
}
class AttributeTypeCommand {
// ...
}
With the example given above, the GSP could reference association indices
<g:textField name="attributes[0].someProperty" ...
This works even for non-existent indices since every get(index) call on LazyList evaluates whether the list already has an element on that position and if not, the list will automatically grow in size and return a new object from the specified factory.
Note that you could also use LazyMap in order to create the similar code with lazy maps:
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LazyMap.html
http://commons.apache.org/collections/apidocs/org/apache/commons/collections/list/LazyList.html
Update:
Groovy 2.0 (which is not yet part of the Grails distribution) will come with embedded support for lazy and eager lists. I wrote a blog post on this topic:
http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/
Update:
With the release of Grails 2.2.0, Groovy 2.0 is part of the distribution.
http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects/