Grails Parent Child Form不保存子数据

时间:2014-08-18 22:50:58

标签: grails data-binding

我正在尝试使用Author和Book域类创建父子表单。视图工作正常,让我在创建新作者时输入书籍。但是,当我添加新作者和书籍并检查数据库(dbconsole)时,我在Author表中看到一条新记录,但没有记录添加到Book表中。你能告诉我我在这里错过了什么或做错了吗?

以下是我的域类:

作者:

package bookauthor1tomany

import org.apache.common.collections.list.*
import org.apache.commons.collections.ListUtils.*

class Author {

    static constraints = {
    }

    String name
    String category

    List<Book> books = new ArrayList<>()
    static hasMany = [ books:Book ]

    static mapping = {
        books cascade:"all-delete-orphan"
    }

    def getExpandableBookList() {
        return LazyList.decorate(books, FactoryUtils.instantiateFactory(Book.class))
    }

    String toString(){
        return "${name}" - "${category}"
    }

}

图书

package bookauthor1tomany

class Book {

    static constraints = {
    }

    String title
    boolean _deleted

    static transients = [ '_deleted' ]

    static belongsTo = [ author:Author ]

    def String toString() {
        return title
    }

}

AuthorController

我没有用控制器改变任何东西。这是Author控制器的默认生成保存方法。

@Transactional
def save(Author authorInstance) {
    if (authorInstance == null) {
        notFound()
        return
    }

    if (authorInstance.hasErrors()) {
        respond authorInstance.errors, view:'create'
        return
    }

    authorInstance.save flush:true

    request.withFormat {
        form multipartForm {
            flash.message = message(code: 'default.created.message', args: [message(code: 'author.label', default: 'Author'), authorInstance.id])
            redirect authorInstance
        }
        '*' { respond authorInstance, [status: CREATED] }
    }
}

GSP的

create.gsp

<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main">
        <g:set var="entityName" value="${message(code: 'author.label', default: 'Author')}" />
        <title><g:message code="default.create.label" args="[entityName]" /></title>
    </head>
    <body>
        <a href="#create-author" class="skip" tabindex="-1"><g:message code="default.link.skip.label" default="Skip to content&hellip;"/></a>
        <div class="nav" role="navigation">
            <ul>
                <li><a class="home" href="${createLink(uri: '/')}"><g:message code="default.home.label"/></a></li>
                <li><g:link class="list" action="index"><g:message code="default.list.label" args="[entityName]" /></g:link></li>
            </ul>
        </div>
        <div id="create-author" class="content scaffold-create" role="main">
            <h1><g:message code="default.create.label" args="[entityName]" /></h1>
            <g:if test="${flash.message}">
            <div class="message" role="status">${flash.message}</div>
            </g:if>
            <g:hasErrors bean="${authorInstance}">
            <ul class="errors" role="alert">
                <g:eachError bean="${authorInstance}" var="error">
                <li <g:if test="${error in org.springframework.validation.FieldError}">data-field-id="${error.field}"</g:if>><g:message error="${error}"/></li>
                </g:eachError>
            </ul>
            </g:hasErrors>
            <g:form url="[resource:authorInstance, action:'save']" >
                <fieldset class="form">
                    <g:render template="authortemp"/>
                </fieldset>
                <fieldset class="buttons">
                    <g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
                </fieldset>
            </g:form>
        </div>
    </body>
</html>

_form.gsp

<%@ page import="BookAuthor1ToMany" %>



<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'books', 'error')} ">
    <label for="books">
        <g:message code="author.books.label" default="Books" />

    </label>

<ul class="one-to-many">
<g:each in="${authorInstance?.books?}" var="b">
    <li><g:link controller="book" action="show" id="${b.id}">${b?.encodeAsHTML()}</g:link></li>
</g:each>
<li class="add">
<g:link controller="book" action="create" params="['author.id': authorInstance?.id]">${message(code: 'default.add.label', args: [message(code: 'book.label', default: 'Book')])}</g:link>
</li>
</ul>


</div>

<div class="fieldcontain ${hasErrors(bean: authorInstance, field: 'name', 'error')} required">
    <label for="name">
        <g:message code="author.name.label" default="Name" />
        <span class="required-indicator">*</span>
    </label>
    <g:textField name="name" required="" value="${authorInstance?.name}"/>

</div>

_authortemp.gsp

<div class="dialog">
    <table>
        <tbody>
            <tr class="prop">
                <td valign="top" class="name"><label for="name">Name:</label></td>
                <td valign="top" class="value ${hasErrors(bean:authorInstance,field:'name','errors')}">
                    <input type="text" id="name" name="name" value="${fieldValue(bean:authorInstance,field:'name')}"/>
                </td>
            </tr>
                        <tr class="prop">
                <td valign="top" class="name"><label for="category">Category:</label></td>
                <td valign="top" class="value ${hasErrors(bean:authorInstance,field:'category','errors')}">
                    <input type="text" id="category" name="category" value="${fieldValue(bean:authorInstance,field:'category')}"/>
                </td>
            </tr>
            <tr class="prop">
                <td valign="top" class="name"><label for="books">Books:</label></td>
                <td valign="top" class="value ${hasErrors(bean:authorInstance,field:'books','errors')}">
                    <g:render template="books" model="['authorInstance':authorInstance]" />
                </td>
            </tr>
        </tbody>
    </table>
</div>

_books.gsp

<script type="text/javascript">
    var childCount = ${authorInstance?.books.size()} + 0;

    function addChild() {
        var htmlId = "book" + childCount;
        var deleteIcon = "${resource(dir:'images/skin', file:'database_delete.png')}";
        var templateHtml = "<div id='" + htmlId + "' name='" + htmlId + "'>\n";
        templateHtml += "<input type='text' id='expandableBookList[" + childCount + "].title' name='expandableBookList[" + childCount + "].title' />\n";
        templateHtml += "<span onClick='$(\"#" + htmlId + "\").remove();'><img src='" + deleteIcon + "' /></span>\n";
        templateHtml += "</div>\n";
        $("#childList").append(templateHtml);
        childCount++;
    }
</script>

<div id="childList">
    <g:each var="book" in="${authorInstance.books}" status="i">
        <g:render template='book' model="['book':book,'i':i]"/>
    </g:each>
</div>
<input type="button" value="Add Book" onclick="addChild();" />

_book.gsp

<div id="book${i}">
    <g:hiddenField name='expandableBookList[${i}].id' value='${book.id}'/>
    <g:textField name='expandableBookList[${i}].title' value='${book.title}'/>
    <input type="hidden" name='expandableBookList[${i}]._deleted' id='expandableBookList[${i}]._deleted' value='false'/>
    <span onClick="$('#expandableBookList\\[${i}\\]\\._deleted').val('true'); $('#expandableBookList${i}').hide()">Delete</span>
</div>

1 个答案:

答案 0 :(得分:0)

您不需要

List<Book> books = new ArrayList<>()

在您的域中。默认情况下,hasMany会为您提供书籍收藏。这可能会导致一些问题。此外,您是否已在控制器中进行调试,以确保在保存时填充书籍集合。