Grails为什么响应不起作用并且什么也不渲染?

标签 grails scaffolding

我有一个域类 TmMessage,我使用 generate-all 来创建脚手架 Controller 和 View 。自动生成的 show() 方法如下所示:

def show(TmMessage tmMessage) {
    respond tmMessage
}

脚手架在我的 BuildConfig.groovy 中定义:
plugins {
    compile ":scaffolding:2.1.2"
}

TmMessage 对象的列表由 Controller 的方法给出:
def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    respond TmMessage.list(params), model:[tmMessageCount: TmMessage.count()]
}

TmMessages 存储在父对象 TmBulkMessage 的 hasMany 列表中,当检查 TmBulkMessage 时,我可以看到列出的 TmMessages 正常。但是,TmMessage 对象列表什么也没有显示(我可以看到很多页的 TmMessage 对象,但没有显示它们的详细信息)。当我单击 TmBulkMessage 中的一个链接以查看特定的 TmMessage 对象时,没有显示任何内容。我相信这是因为显示的 tmMessage 为空。

show() 方法与我在其他地方看到的非常不同,它看起来像(直接取自 Grails 文档):
def show() {
    def book = Book.get(params.id)
    log.error(book)
    [bookInstance : book]
}

自动生成的单元测试都使用第一种方法,请问这是怎么回事?脚手架代码中是否缺少某些内容?

编辑:

从 Grails 文档中,2.3 中的新功能(我使用的是 2.4):

Domain Classes As Command Objects When a domain class is used as a command object and there is an id request parameter, the framework will retrieve the instance of the domain class from the database using the id request parameter.



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

进一步编辑:

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

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>

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>

如果我将 gsp 修改为:
<ol class="property-list tmMessage">
    <% System.out.println "tmMessage : " + tmMessage %>

然后,当我查看页面时,我将“tmMessage:null”写入控制台。

我已将 show() 更改为:
def show(TmMessage tmMessage) {
    respond tmMessage, [model: [tmMessage : tmMessage]]
}

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

最佳答案

我想我现在知道问题所在了:respond有一个非常奇怪的变量命名约定。如果您回复一个 TmMessage例如,变量将被称为 tmMessageInstance在 View 中。如果您使用它们的列表进行响应,则该变量将被称为 tmMessageInstanceList .如果你返回一套......好吧,你知道我的意思。

因此,在上面的 GSP 代码中,您可能可以替换所有 tmMessagetmMessageInstance并摆脱[model: [tmMessage : tmMessage]]在 Controller 中。我的一个习惯是在我编写的每个 GSP 中明确测试每个预期模型变量的存在和类型,如下所示:<% assert tmModelInstance instanceof com.package.TmModel %> .然后这些行作为文档,如果 Controller 将一些意想不到的东西传递到您的 GSP 中(在主动开发期间可能经常发生,特别是在从服务填充数据模型时),您的代码显然会失败并显示一个很好的诊断消息。

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

编辑:显然您可以将 Map 语法与 respond 一起使用并将其用作获取固定变量名称的模型,但文档记录很差:https://github.com/grails/grails-doc/commit/13cacbdce73ca431619362634321ba5f0be570a1

关于Grails为什么响应不起作用并且什么也不渲染?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27248477/

相关文章:

grails - PooledConnection 已关闭

grails - 无法连接 grails 项目中的数据源

grails - 完全覆盖或使用前缀覆盖 Grails 脚手架 URL

php - PHP 脚手架

delphi - 帮助基于数据库表(脚手架?)生成 Delphi DFM

grails - Grails 3.1.6部署到JBoss是404

grails - Grails Spring Security在身份验证失败时看不到闪存消息

Grails 字段插件 : Select tag for one-to-many relationships

c# - 没有支持此项目的脚手架 Visual Studio 2019

ruby-on-rails - 我可以使用 MoSTLy CSS 更改脚手架页面的外观/感觉吗?