3

我想知道在 grails 应用程序中使用命令对象是否有任何首选模式。特别是如果我应该定义另一种方法来保存对象或使用相同的控制器方法来呈现表单和保存?

让我展示一个示例,其中包含用于呈现表单和保存对象的单独方法

def user(int id) {} // shows the edit user form - user.gsp. Submit takes us to saveUser method
def saveUser(UserCommand cmd) {} // actually saves the user, then redirects somewehre else

一切都应该工作,但是:

如果存在验证错误,则该saveUser方法必须执行该user方法所做的整个逻辑。如果在显示用户表单之前,我们必须从数据库加载其他对象,执行一些计算等......它必须再次完成,因为我们必须再次显示表单并包含验证错误。这会导致不必要的代码重复。当验证失败时,我无法重定向到user方法,因为我会丢失命令对象以及与之相关的任何错误。因此我将无法显示验证错误。

使用相同方法保存和呈现表单的另一个示例

def user(UserCommand cmd, Integer id) {
   def u=User.load(id)
   if (request.method=="POST"&&cmd.validate()) {
      // populate u with command object values and save in database
      // then redirect somewhere
   }
}

此示例消除了对代码重复的需要。仅在有POST请求时才保存用户,在其他情况下仅显示表单。如果出现验证错误,它们可以访问并显示在 gsp 页面上。

主要问题是即使有GET对用户页面的请求(这意味着显示 user.gsp 表单),也会创建并验证一个空的命令对象实例(因为它是在同一个控制器中定义的)。因此,每次显示表单时都会出现验证错误,即提供的值为空(因为命令对象为空)

两种场景都可以轻松修改以正常工作(例如:在场景 1 中的会话中重定向之前保存命令对象,并且仅POST在场景 2 中有请求时才显示验证错误)但它需要额外的代码并且似乎不是很优雅.

有没有更简单 - 更多“圣杯”的解决方案来解决这个问题?

4

3 回答 3

1

问题:在控制器操作中复制模型填充逻辑。例如,在多个操作中将作者列表插入模型。

有时我觉得很优雅的解决方案:

def afterInterceptor = { model ->
    model.authors = authorService.list()
}

查看 Grails 文档了解更多信息:http: //grails.org/doc/latest/ref/Controllers/afterInterceptor.html

于 2014-07-31T18:43:55.820 回答
1

这是我在这种情况下通常做的一个示例:

def create(MeetingCommand cmdMtn) {
    switch (request.method) {
    case 'GET':
        cmdMtn = new MeetingCommand() 
        bindData cmdMtn, params, [include: boundProperties]
        createEditModel(cmdMtn: cmdMtn)
        break
    case 'POST':
            def meetingInstance
            Meeting.withTransaction { status ->
                try{
                    Boolean okValidated = (cmdMtn.validate() && !cmdMtn.hasErrors())
                    if (okValidated) {
                        meetingInstance = meetingService.bindInstance(cmdMtn)
                        flashMessage 'meeting.created', FLASH_CLASS_SUCCESS, [meetingInstance.id], 'Meeting creato'  
                        redirect action: ACTION_SHOW, id: meetingInstance.id
                    }
                    else {
                        log.error cmdMtn
                        log.error cmdMtn.errors
                        status.setRollbackOnly()
                        createEditModel(cmdMtn: cmdMtn)
                    }
                } catch (e){
                    status.setRollbackOnly()
                    log.error e
                    createEditModel(cmdMtn: cmdMtn)
                }
            }
        break
    }
}
protected createEditModel(mdl = [:]) {
    mdl
}

我从http://blog.freeside.co/post/41774629876/semi-restful-scaffolded-controllers获得灵感,并且域持久性逻辑已移至服务层。

于 2013-03-21T16:39:35.990 回答
0

我开始向isBlank我的命令对象添加一个吸气剂:

class UserCommand {
    String username
    String password

    boolean isBlank() { !username && !password }
}

然后在我的控制器中,我重新创建空白命令,因此不运行验证:

def createUser(UserCommand cmd) {
    if (cmd.blank) cmd = new UserCommand()
    //...render form, there will be no errors on inital view
}

def saveUser(UserCommand cmd) {
    if (cmd.hasErrors()) {
        return createUser(cmd)
    } else {
    //...and so on
}

唯一的缺点是,如果用户提交一个空白表单,将不会出现验证错误,但另一方面,空白表单应该很明显必须填写一些东西,否则为什么会有呢?

于 2014-12-10T14:39:09.437 回答