我是Groovy和Grails的新手,并试图通过审阅这篇文章来努力实现授权:http://groovy.codehaus.org/Replace+Inheritance+with+Delegation。
使用Netbeans中的默认生成视图,当我遵循继承和委派示例时,它创建了相应的表单字段,但在MOP示例中,它只创建了本地变量字段。如果我使用前面示例生成的表单,我会得到一个StackOverflowError。
2014-03-20 08:39:32,569 [http-bio-8080-exec-7] ERROR errors.GrailsExceptionResolver - StackOverflowError occurred when processing request: [GET] /Delegation/attendee/create
Stacktrace follows:
Message: Error processing GroovyPageView: Error executing tag <g:form>: Error executing tag <g:render>: null
Line | Method
->> 530 | doFilter in C:\development\sandbox\Delegation\grails-app\views\attendee\create.gsp
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Caused by GrailsTagException: Error executing tag <g:form>: Error executing tag <g:render>: null
->> 36 | doCall in C:/development/sandbox/Delegation/grails-app/views/attendee/create.gsp
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Caused by GrailsTagException: Error executing tag <g:render>: null
->> 31 | doCall in C:/development/sandbox/Delegation/grails-app/views/attendee/create.gsp
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Caused by StackOverflowError: null
->> 2037 | get in java.util.Collections$SynchronizedMap
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
| 11 | doCall in delegation.Attendee$_hasLocalProperty_closure2
| 29 | getProperty in delegation.Attendee
| 30 | getProperty in ''
....
|Compiling 1 source files
如何将视图连接到MOP示例?
更新:添加视图,域类和控制器类
域:
class Attendee {
enum AttendeeRole {
VENDOR, PRESENTER, STUDENT
}
private person = new Person()
private hasLocalProperty(name) {
metaClass.properties.collect{ it.name }.contains(name)
}
AttendeeRole role
Attendee (Map map) {
map.each{ k, v -> setProperty(k, v) }
}
void setProperty(String name, value) {
if (hasLocalProperty(name)) this.@"$name" = value
else delegate.setProperty(name, value)
}
def getProperty(String name) {
if (hasLocalProperty(name)) return this.@"$name"
else return delegate.getProperty(name)
}
}
人:
class Person {
String firstName
String lastName
Integer age
static constraints = {
lastName blank: false
firstName blank: false
age min: 18
}
def describe() {"$firstName $lastName is $age"}
}
查看(在继承时生成):
<%@ page import="delegation.Attendee" %>
<div class="fieldcontain ${hasErrors(bean: attendeeInstance, field: 'lastName', 'has-error')} required form-group">
<label for="lastName" class="control-label col-sm-2">
<g:message code="attendee.lastName.label" default="Last Name" />
<span class="required-indicator">*</span>
</label>
<div class="col-sm-3">
<g:textField class="form-control" name="lastName" required="" value="${attendeeInstance?.lastName}"/>
</div>
</div>
<div class="fieldcontain ${hasErrors(bean: attendeeInstance, field: 'firstName', 'has-error')} required form-group">
<label for="firstName" class="control-label col-sm-2">
<g:message code="attendee.firstName.label" default="First Name" />
<span class="required-indicator">*</span>
</label>
<div class="col-sm-3">
<g:textField class="form-control" name="firstName" required="" value="${attendeeInstance?.firstName}"/>
</div>
</div>
<div class="fieldcontain ${hasErrors(bean: attendeeInstance, field: 'age', 'has-error')} required form-group">
<label for="age" class="control-label col-sm-2">
<g:message code="attendee.age.label" default="Age" />
<span class="required-indicator">*</span>
</label>
<div class="col-sm-3">
<g:field class="form-control" name="age" type="number" min="18" value="${attendeeInstance.age}" required=""/>
</div>
</div>
<div class="fieldcontain ${hasErrors(bean: attendeeInstance, field: 'role', 'has-error')} required form-group">
<label for="role" class="control-label col-sm-2">
<g:message code="attendee.role.label" default="Role" />
<span class="required-indicator">*</span>
</label>
<div class="col-sm-3">
<g:select class="form-control" name="role" from="${delegation.Attendee$AttendeeRole?.values()}" keys="${delegation.Attendee$AttendeeRole.values()*.name()}" required="" value="${attendeeInstance?.role?.name()}" />
</div>
</div>
使用MOP生成的视图:
<%@ page import="delegation.Attendee" %>
<div class="fieldcontain ${hasErrors(bean: attendeeInstance, field: 'role', 'has-error')} required form-group">
<label for="role" class="control-label col-sm-2">
<g:message code="attendee.role.label" default="Role" />
<span class="required-indicator">*</span>
</label>
<div class="col-sm-3">
<g:select class="form-control" name="role" from="${delegation.Attendee$AttendeeRole?.values()}" keys="${delegation.Attendee$AttendeeRole.values()*.name()}" required="" value="${attendeeInstance?.role?.name()}" />
</div>
</div>
控制器类:
import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional
@Transactional(readOnly = true)
class AttendeeController {
static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
def index(Integer max) {
params.max = Math.min(max ?: 10, 100)
respond Attendee.list(params), model:[attendeeInstanceCount: Attendee.count()]
}
def show(Attendee attendeeInstance) {
respond attendeeInstance
}
def create() {
respond new Attendee(params)
}
@Transactional
def save(Attendee attendeeInstance) {
if (attendeeInstance == null) {
notFound()
return
}
if (attendeeInstance.hasErrors()) {
respond attendeeInstance.errors, view:'create'
return
}
attendeeInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'attendeeInstance.label', default: 'Attendee'), attendeeInstance.id])
redirect attendeeInstance
}
'*' { respond attendeeInstance, [status: CREATED] }
}
}
def edit(Attendee attendeeInstance) {
respond attendeeInstance
}
@Transactional
def update(Attendee attendeeInstance) {
if (attendeeInstance == null) {
notFound()
return
}
if (attendeeInstance.hasErrors()) {
respond attendeeInstance.errors, view:'edit'
return
}
attendeeInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'Attendee.label', default: 'Attendee'), attendeeInstance.id])
redirect attendeeInstance
}
'*'{ respond attendeeInstance, [status: OK] }
}
}
@Transactional
def delete(Attendee attendeeInstance) {
if (attendeeInstance == null) {
notFound()
return
}
attendeeInstance.delete flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.deleted.message', args: [message(code: 'Attendee.label', default: 'Attendee'), attendeeInstance.id])
redirect action:"index", method:"GET"
}
'*'{ render status: NO_CONTENT }
}
}
protected void notFound() {
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.not.found.message', args: [message(code: 'attendeeInstance.label', default: 'Attendee'), params.id])
redirect action: "index", method: "GET"
}
'*'{ render status: NOT_FOUND }
}
}
}