Grails dynamic scaffold with hasMany: is it a bug or am I misconfiguring?
Asked Answered
R

3

7

I'm a Grails noob and running into something that seems to be a bug, but it is entirely possible I'm not configuring everything correctly.

I've got two simple Domain Classes:

   class Player {

        String firstName
        String lastName

        static constraints = {
            firstName(blank:false)
            lastName(blank:false)
        }
        String toString() { lastName + ", " + firstName }
    }

and

class Team {

    String mascot;
    static hasMany = [players:Player]

    static constraints = {
        mascot(blank:false)
    }
}

I have controllers for both that do nothing beyond dynamic scaffold these two Domain Classes.

But even when I have a list of Players in my DB, I don't get a multi-select box for them when creating a new Team.

However, the multi-select shows up when I go to edit a Team

Is this a bug in the dynamic scaffolding for new items, do I misunderstand how this is supposed to work, or is there something else I need to declare here?

Any help is hugely appreciated! I've got screenshots that StackOverflow won't let me add because of my newness, but I'd be happy to show them another way if that'll help.

Renfred answered 5/5, 2009 at 3:6 Comment(0)
R
2

I finally figured this out and wanted to pass on what I did just in case someone else runs into it.

When I generated the views for Team, the form block in edit.gsp looks like this:

    <input type="hidden" name="id" value="${teamInstance?.id}" />
                <input type="hidden" name="version" value="${teamInstance?.version}" />
                <div class="dialog">
                    <table>
                        <tbody>

                            <tr class="prop">
                                <td valign="top" class="name">
                                    <label for="mascot">Mascot:</label>
                                </td>
                                <td valign="top" class="value ${hasErrors(bean:teamInstance,field:'mascot','errors')}">
                                    <input type="text" id="mascot" name="mascot" value="${fieldValue(bean:teamInstance,field:'mascot')}"/>
                                </td>
                            </tr> 

                            <tr class="prop">
                                <td valign="top" class="name">
                                    <label for="players">Players:</label>
                                </td>
                                <td valign="top" class="value ${hasErrors(bean:teamInstance,field:'players','errors')}">
                                    <g:select name="players"
from="${Player.list()}"
size="5" multiple="yes" optionKey="id"
value="${teamInstance?.players}" />

                                </td>
                            </tr> 

                        </tbody>
                    </table>
                </div>
                <div class="buttons">
                    <span class="button"><g:actionSubmit class="save" value="Update" /></span>
                    <span class="button"><g:actionSubmit class="delete" onclick="return confirm('Are you sure?');" value="Delete" /></span>
                </div>
            </g:form>

but the form block in create.gsp looks like this:

<g:form action="save" method="post" >
                <div class="dialog">
                    <table>
                        <tbody>

                            <tr class="prop">
                                <td valign="top" class="name">
                                    <label for="mascot">Mascot:</label>
                                </td>
                                <td valign="top" class="value ${hasErrors(bean:teamInstance,field:'mascot','errors')}">
                                    <input type="text" id="mascot" name="mascot" value="${fieldValue(bean:teamInstance,field:'mascot')}"/>
                                </td>
                            </tr> 

                        </tbody>
                    </table>
                </div>
                <div class="buttons">
                    <span class="button"><input class="save" type="submit" value="Create" /></span>
                </div>
        </g:form>

In other words, for this corner case, the default Create view omits the widget to properly display the multi-select list. When I did a copy and paste of the missing code, the dynamically scaffolded controller picked it up and persisted it as expected. So, it's definitely a bug in the view generation code.

Renfred answered 6/5, 2009 at 22:30 Comment(0)
T
1

Yes, the default scaffolding puts a parent selector in the child class' create/edit page.

I'm guessing it was just easier for them this way. It shouldn't be a multi-select though, just a pull-down single-select, as it's a One-to-Many.

As you've explained you wanted more of a Many-to-Many relationship, you might try adding:

static hasMany = [teams:Team]

to your Player class. I've found that Grails does better with bi-directional relationships. It's also useful to have when building search queries, and shouldn't require more than the one relationship table you'd already need.

If you're using Grails pre-v1.1, Many-to-Many relationships aren't directly supported, so even adding the static hasMany above won't be the complete solution, as you'll need to manage adding to the other list when you add to one direction. I haven't used v1.1 yet, so I can't talk about what is needed to specify the Many-to-Many in it.

Tetanic answered 5/5, 2009 at 6:10 Comment(3)
Thanks for your reply Bill. I think perhaps where my example could be better, is that I'd like a Player to be a member of more than one Team. It's really more many-to-many than one-to-many. This is why I don't have a belongsTo on the Player, but I see now that the use case I happened to choose might have caused confusion. The thing that's weird to me is that the multi-select shows up on the Edit screen as expected, but not on the Create screen. That inconsistency makes me think it's a bug in dynamic scaffolding but I'm hardly beyond making mistakes (clearly)Renfred
Hmm, up until v1.1, Grails didn't really support Many-to-Many, requiring you to manually create a relationship object and manage the reciprocals yourself. I haven't used v1.1 yet, so I can't comment on its scaffolding defaults, but the fact that it shows on edit, but not create, does seem fishy. You might try posting on Nabble's Grails email list: nabble.com/grails---user-f11861.htmlTetanic
I've edited my response to take your Many-to-Many facet into accountTetanic
T
1

I encountered the same problem using current version (v1.3.4) of Grails. Had to manually modify the create.gsp

Tramway answered 28/9, 2010 at 2:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.