grailsscaffoldinggrails-2.5

Grails 2.5 scaffolding does not remove last hasMany during bind


I created a new Grails 2.5.1 project with 2 domains, and used generate-all to create the scaffolding. One of the domains hasMany of the other domain: User hasMany roles. When I run the app and create a new user and select a role for the user, it adds it. I can add multiple roles for a user, or remove them, except for the last one. I can't remove the last one. If I try to remove all the roles for a user it ignores it and leaves the roles that were selected previously. (I found the same behavior in Grails 2.4.4.)

Domains:

class Role {
    String name
}

class User {
    String name
    static hasMany = [roles: Role]
}

The scaffolding view creates the html select box with:

<g:select name="roles" from="${myapp.Role.list()}" 
    multiple="multiple" optionKey="id" size="5"
    value="${userInstance?.roles*.id}" class="many-to-many"/>

Creating this html:

<select id="roles" class="many-to-many" size="5" multiple="multiple" name="roles">
<option selected="selected" value="1">myapp.Role : 1</option>
<option value="2">myapp.Role : 2</option>
</select>

To reproduce:

  1. Run the app
  2. Create a few roles
  3. Create a new user, enter a name, select one or more roles, and click Create
  4. Edit the user, unselect the roles, and click Update
  5. Note that the previous roles are still selected

I understand that nothing is submitted with the form for that field if nothing is selected. I can fix it by doing user.roles.clear() before binding. That works, but I'm wondering...

Is the scaffolding really not designed to handle this case? Or, am I doing something wrong, like in the way my domains are coded? Is there a better solution than editing the scaffolding to clear the roles before binding?


Solution

  • I am not sure how do you unselect the roles as there will be at least one role selected on the form always while editing if you had added a role during creation.

    You would have to change scaffolded views to achieve what you want. Use noSelection property of g:select to specify what should be the default value if user doesn't want to select any existing role.

    Your g:select tag then would be:

    <g:select name="roles" from="${myapp.Role.list()}" 
        noSelection="['': '--NoSelection--']"
        multiple="multiple" optionKey="id" size="5"
        value="${userInstance?.roles*.id}" class="many-to-many"/>
    

    And Scaffolding lets you generate some basic CRUD interfaces for a domain class. It doesn't support a lot of things ans you have to implement them on your own.