Grails - Removing an item from a hasMany association List on data bind?

ecrane picture ecrane · Apr 28, 2010 · Viewed 24.7k times · Source

Grails offers the ability to automatically create and bind domain objects to a hasMany List, as described in the grails user guide.

So, for example, if my domain object "Author" has a List of many "Book" objects, I could create and bind these using the following markup (from the user guide):

<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" /> 

In this case, if any of the books specified don't already exist, Grails will create them and set their titles appropriately. If there are already books in the specified indices, their titles will be updated and they will be saved. My question is: is there some easy way to tell Grails to remove one of those books from the 'books' association on data bind?

The most obvious way to do this would be to omit the form element that corresponds to the domain instance you want to delete; unfortunately, this does not work, as per the user guide:

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

I realize that a specific solution could be engineered as part of a command object, or as part of a particular controller- however, the need for this functionality appears repeatedly throughout my application, across multiple domain objects and for associations of many different types of objects. A general solution, therefore, would be ideal. Does anyone know if there is something like this included in Grails?

Answer

mlathe picture mlathe · Dec 14, 2010

Just ran into this issue myself. It's easy to solve. Grails uses java.util.Set to represent lists. You can just use the clear() method to wipe the data, and then add in the ones you want.

//clear all documents
bidRequest.documents.clear()

//add the selected ones back
params.documentId.each() {
    def Document document = Document.get(it)
    bidRequest.documents.add(document)
    log.debug("in associateDocuments: added " + document)
};

//try to save the changes
if (!bidRequest.save(flush: true)) {
    return error()
} else {
    flash.message = "Successfully associated documents"
}

I bet you can do the same thing by using the "remove()" method in the case that you don't want to "clear()" all the data.