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

Pete picture Pete · May 5, 2009 · Viewed 7.5k times · Source

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.

Answer

Pete picture Pete · May 7, 2009

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.