我有一个域类TmMessage,我使用generate-all来创建脚手架控制器和视图。自动生成的show()方法如下所示:
def show(TmMessage tmMessage) {
respond tmMessage
}
脚手架在我的BuildConfig.groovy中定义:
plugins {
compile ":scaffolding:2.1.2"
}
TmMessage对象列表由控制器方法提供:
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 docs):
def show() {
def book = Book.get(params.id)
log.error(book)
[bookInstance : book]
}
自动生成的单元测试全部使用第一种方法,所以请问这里有什么?脚手架代码中是否缺少某些内容?
编辑:
从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…"/></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 %>
然后我得到&#34; tmMessage:null&#34;在我查看页面时写入控制台。
我已将show()更改为:
def show(TmMessage tmMessage) {
respond tmMessage, [model: [tmMessage : tmMessage]]
}
这似乎解决了show的渲染问题。我不知道index()需要改变什么。当我选择&#34;编辑&#34;从显示页面,我得到一个消息字段的空白文本字段,我不知道这是否是预期的行为,但如果该字段预先加载了现有值,则会更受欢迎。
答案 0 :(得分:7)
我想我现在知道问题是什么:respond
有一个非常奇怪的变量命名约定。如果您使用单个TmMessage
实例进行响应,则视图中的变量将被称为tmMessageInstance
。如果您回复它们的列表,则该变量将被称为tmMessageInstanceList
。如果你退回一套...好吧,你知道我的意思。
因此,在上面的GSP代码中,您可以将所有tmMessage
替换为tmMessageInstance
并删除控制器中的[model: [tmMessage : tmMessage]]
。我的习惯是在我写的每个GSP中明确地测试每个预期模型变量的存在和类型,如:<% assert tmModelInstance instanceof com.package.TmModel %>
。然后这些行作为文档,如果控制器将意外事件传递给您的GSP(在活动开发期间经常发生,特别是在从服务填充数据模型时),您的代码会很明显地失败并带有很好的诊断消息。
在我看来,Grails的一个更好的选择是坚持respond
渲染器的单个变量(例如model
),在几个地方记录它只是为了没有人错过这个,然后人们可以在必要时检测那里有什么(无论如何你不知道你是否会有一个列表或单个视图/模板的单个实例?)。
编辑:显然你可以使用respond
的Map语法并将其用作获取固定变量名称的模型,它只是记录不清:https://github.com/grails/grails-doc/commit/13cacbdce73ca431619362634321ba5f0be570a1
答案 1 :(得分:1)
感谢Gregor,他的帮助让我走上正轨,问题在于生成的代码。看起来没有一个模型被传递给视图,因此它什么也没有渲染。以下是对index(),show()edit()
的更改def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond TmMessage.list(params), model:[tmMessageCount: TmMessage.count(), tmMessageList : TmMessage.list(params)]
}
def show(TmMessage tmMessage) {
respond tmMessage, [model: [tmMessage: tmMessage]]
}
def edit(TmMessage tmMessage) {
respond tmMessage, [model: [tmMessage: tmMessage]]
}
这会使用正确的值预加载文本字段。
我还必须通过将模型与所需视图一起传递来修改在创建时发生错误时发送的参数。以下是save()的示例:
@Transactional
def save(TmMessage tmMessage) {
if (tmMessage == null) {
notFound()
return
}
if (tmMessage.hasErrors()) {
respond tmMessage.errors, [view:'create', model: [tmMessage: tmMessage]]
return
}
tmMessage.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'tmMessage.label', default: 'TmMessage'), tmMessage.id])
redirect tmMessage
}
'*' { respond tmMessage, [status: CREATED] }
}
}
答案 2 :(得分:1)
当我在我的域模型中继承时,发生了这种情况。
例如,如果我们有
class Vehicle {}
和
class Car extends Vehicle {}
当视图尝试渲染 vehicleInstanceList 时,脚手架控制器操作将 carInstanceList 传递到视图中。
如前面的答案中所述,响应方法按惯例创建变量名称,约定似乎在这里失败
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Vehicle.list(params), model:[vehicleInstanceCount: Vehicle.count()] //actually injecting carInstanceList
}
不得不改为:
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
def vehicles = Vehicle.list(params)
respond vehicles, model:[vehicleInstanceCount: Vehicle.count(), vehicleInstanceList:vehicles]
}
我认为这与检查列表中第一个元素的类有关,如果是汽车,则将其命名为carInstanceList,如果第一个是车辆,则问题可能不会出现