zoran119
zoran119

Reputation: 11307

a list of checkboxes

I have two domain classes

class Contract {
    String number
    static hasMany = [statements:Statement]
}

class Statement {
    String code
    static hasMany = [contracts:Contract]
}

I would like to show all statements available in my gsp with a checkbox next to each, allowing the user to choose which statements are applicable to the contract. So something like:

[ ] Statement Code 1
[ ] Statement Code 2
[ ] Statement Code 3

I started off with this:

<g:each in="${Statement.list()}" var="statement" status="i">
    <g:checkBox name="statements[${i}].id" value="${statement.id}" checked="${contractInstance.statements.contains(statement.id)}" />
    <label for="statements[${i}]">${statement.code}</label>
</g:each>

But i just cannot get a list of checked statements to the controller (there are null elements in the list, there are repeated statements...).

Any idea how to achieve this?

Upvotes: 9

Views: 12032

Answers (4)

Wytze
Wytze

Reputation: 1565

I personally prefer to get the list of Id's in this case.

<g:each var="book" in="${books}">
    <g:checkBox name="bookIds" value="${book.id}" ...
</g:each>

Command Object:

class BookCommand {
    List<Serializable> bookIds
}

In controller action:

BookCommand bc ->
    author.books = Book.getAll(bc.bookIds)

Upvotes: 2

Peter Ledbrook
Peter Ledbrook

Reputation: 4482

This is possible, but it does require a bit of a hack. First off, every checkbox must have the same name, "statements":

<g:each in="${org.example.Statement.list(sort: 'id', order: 'asc')}" var="statement" status="i">
    <g:checkBox name="statements" value="${statement.id}" checked="${contract.statements.contains(statement)}" />
    <label for="statements">${statement.content}</label>
</g:each>

Second, in the controller you have to remove the "_statements" property before binding:

def contract = Contract.get(params.id)
params.remove "_statements"
bindData contract, params
contract.save(failOnError: true)

The check box support hasn't been designed for this use case, hence the need for a hack. The multi-select list box is the one typically used for this type of scenario.

Upvotes: 6

sikrip
sikrip

Reputation: 681

Change the checkbox to something like this.

<g:checkBox name="statements.${statement.id}" value="true" checked="${contractInstance.statements.contains(statement)?:''}" />

and then in the controller, in params.statements you will get a list with the IDs of the checked statements.

Also notice the ?:'' in the checked property, it's a good idea to add it because any value(even 'false') in the checked property is interpreted as checked.

Upvotes: 1

Igor Artamonov
Igor Artamonov

Reputation: 35961

Are you mapping request directly to Contract? It's much more secure to map incoming request into an Command object.

As about mapping a list - values are mapped only to existing elements. I mean it cannot create new list elements. You need to prepare it before mapping. If you know that there is always 3 elements, you can make:

class ContractCommand {

   List statements = [
       new Statement(),
       new Statement(),
       new Statement(),
   ]
}

and map request to this object

Upvotes: 0

Related Questions