Grails Parent Child Form не записва дъщерни данни

Опитвам се да създам родителска дъщерна форма с класове на домейн Author и Book. Изгледът работи добре и ми позволява да въвеждам книги, когато създавам нов автор. Въпреки това, когато добавя нов автор и книгите и проверявам базата данни (dbconsole), виждам нов запис в таблицата с автори, но не се добавят записи към таблицата с книги. Можете ли да ме уведомите какво пропускам или правя грешно тук?

Ето моите класове на домейн:

АВТОР:

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="/bg#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="/bg${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>

person user2597565    schedule 18.08.2014    source източник
comment
Също така искам да добавя, че в момента използвам Grails 2.4.2   -  person user2597565    schedule 19.08.2014


Отговори (1)


Не се нуждаете от

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

във вашия домейн. hasMany ще ви даде колекция от книги по подразбиране. Това може да причинява някои проблеми. Също така, отстранили ли сте грешки във вашия контролер, за да се уверите, че колекцията от книги е попълнена, когато се извърши записването.

person cloudwalker    schedule 19.08.2014
comment
Опитах се да премахна декларацията, както беше предложено, но изглежда също не работи. Мисля, че това изявление не прави това, което трябва да LazyList.decorate(books, FactoryUtils.instantiateFactory(Book.class)). Освен това възниква грешка, когато се опитвам да импортирам org.apache.common.collections.list.LazyList в моя Author.groovy. Трябва ли изрично да го включа в моя клас/път за изграждане? - person user2597565; 20.08.2014