0

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?

tbird
  • 123
  • 10
  • try adding the `belongsTo=[user:User]` on the Role Domain Model. There's a line in the documentation **_The default cascading behaviour is to cascade saves and updates, but not deletes unless a belongsTo is also specified_**; I'm not sure it will work but it's worth giving it a shot. If this does not work I'd suggest calling `user.roles.clear()` before binding just as you proposed. – ionutab Aug 17 '15 at 22:59
  • @ionutab: I want a many-to-many relationship. Multiple users can all have the same role. And, a user can have many roles, of course. So, I can't use belongsTo. – tbird Aug 17 '15 at 23:37
  • If you want a many-to-many relationship than the code presented above is not correct. You should also have the hasMany=[] adnotation on the Role Domain Model. Also, In the Role class you need to specify the owner of the relationship, User, in your case using a belongsTo map. [read here](https://grails.github.io/grails-doc/latest/guide/GORM.html#manyToMany) – ionutab Aug 18 '15 at 13:51
  • I didn't code it as bidirectional, yet it functions as a many-to-many, just without the convenience of having easy bidirectional access. Everything behaves correctly, except the scaffolding will not remove the last reference from the Set of roles. I could use belongsTo if I made it bidirectional. I tried your suggestion, and rebuilt the scaffolding, but it behaves the same as far as not being able to remove the last role. I found the docs specifically say scaffolding doesn't support many-to-many. – tbird Aug 18 '15 at 21:27

1 Answers1

0

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.

Sandeep Poonia
  • 2,158
  • 3
  • 16
  • 29
  • You're right, "[Scaffolding feature does not currently support many-to-many](http://grails.github.io/grails-doc/2.5.1/guide/GORM.html#manyToMany)". Your `noSelection` fix works if the user actually selects it. If they unselect every option, it's the same problem. And, if you multi-select both the "no selection" and a role, it gives a "Could not find matching constructor" error. Javascript could fix all this, but gets messy. The `clear()` solution seems better since it works with no selections and does not give an error in any case. – tbird Aug 18 '15 at 21:14