Grails父-子表单不保存子数据

Grails父-子表单不保存子数据,grails,data-binding,Grails,Data Binding,我正在尝试使用Author和Book域类创建父-子表单。该视图工作正常,允许我在创建新作者时输入书籍。但是,当我添加新的作者和书籍并检查数据库(dbconsole)时,我在author表中看到一条新记录,但没有记录添加到Book表中。你能告诉我我错过了什么或做错了什么吗 以下是我的域类: 作者: package bookauthor1tomany import org.apache.common.collections.list.* import org.apache.commons.coll

我正在尝试使用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}"
    }

}
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

创建.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>

${flash.message}
_表1.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>

  • ${b?.encodeAsHTML()}
  • ${message(代码:'default.add.label',args:[消息(代码:'book.label',默认:'book')]}
*
_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>

姓名:
类别:
书:
_普惠制

<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();" />

var childCount=${authorInstance?.books.size()}+0;
函数addChild(){
var htmlId=“book”+儿童计数;
var deleteIcon=“${resource(dir:'images/skin',file:'database_delete.png')}”;
var templateHtml=“\n”;
templateHtml+=“\n”;
templateHtml+=“\n”;
templateHtml+=“\n”;
$(“#childList”).append(templateHtml);
childCount++;
}
_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>

删除
您不需要

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

在您的域中。默认情况下,hasMany将为您提供一个图书集合。这可能会引起一些问题。另外,您是否在控制器中进行了调试,以确保在保存时填充books集合。

另外,我想补充一点,我目前正在使用Grails 2.4.2。我尝试按照建议删除声明,但似乎也不起作用。我认为这句话并没有达到“LazyList.decoration(books,FactoryUtils.instancefactory(Book.class))”的目的。此外,当我尝试在Author.groovy中导入org.apache.common.collections.list.LazyList时,它也会出错。我必须在类/构建路径中显式地包含它吗?
List<Book> books = new ArrayList<>()