Grails为什么回应不起作用并且什么都不做?

Joh*_*ohn 1 grails scaffolding

我有一个域类TmMessage,我使用generate-all来创建脚手架控制器和视图.自动生成的show()方法如下所示:

def show(TmMessage tmMessage) {
    respond tmMessage
}
Run Code Online (Sandbox Code Playgroud)

脚手架在我的BuildConfig.groovy中定义:

plugins {
    compile ":scaffolding:2.1.2"
}
Run Code Online (Sandbox Code Playgroud)

TmMessage对象列表由控制器的方法给出:

def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    respond TmMessage.list(params), model:[tmMessageCount: TmMessage.count()]
}
Run Code Online (Sandbox Code Playgroud)

TmMessages存储在父对象TmBulkMessage的hasMany列表中,我可以在检查TmBulkMessage时看到列出的TmMessages.但是,TmMessage对象列表不显示任何内容(我可以看到TmMessage对象的多个页面,但不显示它们的详细信息).当我单击TmBulkMessage中的一个链接来查看特定的TmMessage对象时,不会显示任何内容.我相信那是因为显示的tmMessage为空.

show()方法与我在别处看到的非常不同,它看起来像(直接来自Grails docs):

def show() {
    def book = Book.get(params.id)
    log.error(book)
    [bookInstance : book]
}
Run Code Online (Sandbox Code Playgroud)

自动生成的单元测试都使用第一种方法,所以请问这里发生了什么?脚手架代码中是否缺少某些东西?

编辑:

从Grails文档中,2.3中的新内容(我正在使用2.4):

域类作为命令对象 当域类用作命令对象并且存在id请求参数时,框架将使用id请求参数从数据库中检索域类的实例.

因此,Grails提供的域类/命令对象接口似乎返回null.

进一步编辑:

感谢Gregor的帮助,看起来域对象绑定工作正常,但响应没有像宣传的那样工作.

show.gsp如下:

<%@ page import="com.example.TmMessage" %>
<!DOCTYPE html>
<html>
    <head>
        <meta name="layout" content="main">
        <g:set var="entityName" value="${message(code: 'tmMessage.label', default: 'TmMessage')}" />
        <title><g:message code="default.show.label" args="[entityName]" /></title>
    </head>
    <body>
        <a href="#show-tmMessage" 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>
                <li><g:link class="create" action="create"><g:message code="default.new.label" args="[entityName]" /></g:link></li>
            </ul>
        </div>
        <div id="show-tmMessage" class="content scaffold-show" role="main">
            <h1><g:message code="default.show.label" args="[entityName]" /></h1>
            <g:if test="${flash.message}">
            <div class="message" role="status">${flash.message}</div>
            </g:if>
            <ol class="property-list tmMessage">

                <g:if test="${tmMessage?.bulkMessage}">
                <li class="fieldcontain">
                    <span id="bulkMessage-label" class="property-label"><g:message code="tmMessage.bulkMessage.label" default="Bulk Message" /></span>

                        <span class="property-value" aria-labelledby="bulkMessage-label"><g:link controller="tmBulkMessage" action="show" id="${tmMessage?.bulkMessage?.id}">${tmMessage?.bulkMessage?.encodeAsHTML()}</g:link></span>

                </li>
                </g:if>

                <g:if test="${tmMessage?.message}">
                <li class="fieldcontain">
                    <span id="message-label" class="property-label"><g:message code="tmMessage.message.label" default="Message" /></span>

                        <span class="property-value" aria-labelledby="message-label"><g:fieldValue bean="${tmMessage}" field="message"/></span>

                </li>
                </g:if>

            </ol>
            <g:form url="[resource:tmMessage, action:'delete']" method="DELETE">
                <fieldset class="buttons">
                    <g:link class="edit" action="edit" resource="${tmMessage}"><g:message code="default.button.edit.label" default="Edit" /></g:link>
                    <g:actionSubmit class="delete" action="delete" value="${message(code: 'default.button.delete.label', default: 'Delete')}" onclick="return confirm('${message(code: 'default.button.delete.confirm.message', default: 'Are you sure?')}');" />
                </fieldset>
            </g:form>
        </div>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

show()中的tmMessage?.dump()的输出是:

<com.example.TmMessage@6d6cf0a5 message=abc errors=grails.validation.ValidationErrors: 0 errors $changedProperties=null id=1 version=0 bulkMessage=com.example.TmBulkMessage : 1>
Run Code Online (Sandbox Code Playgroud)

如果我修改gsp阅读:

<ol class="property-list tmMessage">
    <% System.out.println "tmMessage : " + tmMessage %>
Run Code Online (Sandbox Code Playgroud)

然后,当我查看页面时,我将"tmMessage:null"写入控制台.

我已将show()更改为:

def show(TmMessage tmMessage) {
    respond tmMessage, [model: [tmMessage : tmMessage]]
}
Run Code Online (Sandbox Code Playgroud)

这似乎解决了show的渲染问题.我不知道index()需要改变什么.当我从显示页面选择"编辑"时,我得到一个消息字段的空白文本字段,我不知道这是否是预期的行为,但如果该字段预先加载了现有值,那将更为可取.

Gre*_*rin 7

我想我现在知道问题是什么:respond有一个非常奇怪的变量命名约定.如果您使用单个TmMessage实例进行响应,则将tmMessageInstance在视图中调用该变量.如果您回复它们的列表,则将调用该变量tmMessageInstanceList.如果你回来一套...好吧,你知道我的意思.

因此,在GSP以上代码你也许可以取代所有tmMessagetmMessageInstance和摆脱[model: [tmMessage : tmMessage]]控制器.我的习惯是在我写的每个GSP中明确地测试每个预期模型变量的存在和类型,如下所示:<% assert tmModelInstance instanceof com.package.TmModel %>.这些行然后用作文档,如果控制器将意外事件传递给您的GSP(在活动开发期间经常发生,特别是在从服务填充数据模型时),您的代码会很明显地失败,并且有一个很好的诊断消息.

在我看来,Grails的一个更好的选择是坚持respond渲染器的单个变量(例如model),在几个地方记录它只是让没有人错过这个,然后人们可以在必要时检测到那里的东西(多久发生一次)无论如何,你不知道你是否会有单个视图/模板的列表或单个实例?).

编辑:显然你可以使用Map语法respond并将其用作获取固定变量名称的模型,它只是记录不清:https://github.com/grails/grails-doc/commit/13cacbdce73ca431619362634321ba5f0be570a1

  • 这种基本行为无证是悲剧性的!请投票支持[本期](https://jira.grails.org/browse/GRAILS-10721)以解决此问题. (2认同)