【问题标题】:Grails rejectValue - multiple checks causing ob.errors nullGrails rejectValue - 多次检查导致 ob.errors null
【发布时间】:2016-09-21 07:29:23
【问题描述】:

我的域对象预订有多个允许为空的属性,因为它们将在对象保存到数据库后稍后设置。

myService.action() 的一部分:

booking.properties = params    

if (booking.contactFirstname?.length() <= 1) { booking.errors.rejectValue("contactFirstname", "empty") }
if (booking.contactLastname?.length() <= 1) { booking.errors.rejectValue("contactLastname", "empty") }
if (booking.contactPhone?.length() <= 1) { booking.errors.rejectValue("contactPhone", "empty") }
if (booking.contactMobile?.length() <= 1) { booking.errors.rejectValue("contactMobile", "empty") }
if (booking.contactEmail?.length() <= 1) { booking.errors.rejectValue("contactEmail", "empty") }    

if (booking.hasErrors() || ! booking.validate()) { 
    return [success: false, model: booking]
} else {
    booking.save(failOnError: true)
    return [success: true, model: booking]                
}

我的控制器会:

def result = myService.action(params)
if (result.success) {
    flash.success = message(code: "msg.successfullySaved")
    redirect(action: "registerEventConfirmation", id: result.model.uid, params: [lang: params.lang], mapping: "paginated")   
} else {
    flash.error = message(code: "msg.errorSavingCheckFields")
    render(view: "registerEventStep3", params: [lang: params.lang], model: [booking: result.model])

我正在使用 hasErrors(bean: booking,field:'contactFirstname', 'has-error')}

标记错误字段。

如果我现在提交的表单在文本字段中没有任何值,所有字段都是红色的,booking.errors 有 >0 个错误。

如果我在使用名字之后提交表单,则 booking.errors 为 NULL,并且没有标记其他字段。

这是一个错误吗?我使用 Grails 2.3.6

其他信息

  1. 我访问了表单,提交时完全是空的
  2. 我看到所有表单字段都是红色的,object.errors 有 >0 个错误 (VALID)
  3. 我在第一个字段中输入一个值,名字并提交
  4. 我没有看到任何红色的表单域,object.errors =0 错误(无效)
  5. 我重新提交表单,没有任何更改
  6. 我看到所有空的表单字段都是红色的,object.errors 有 >0 个错误 (VALID)

【问题讨论】:

    标签: grails


    【解决方案1】:

    现在我完全了解了情况,而且由于我难以入睡,我想我给你一个非常简洁的答案,这样你就可以充分理解并正确使用东西。

    首先,我知道创建一个验证 bean 听起来需要做很多工作,所以让我教你如何相对简单地完成这一切,以及为什么它是我的首选方法。

    这是我的首选方法,因为当你这样做时

    类 MyController {

     def myAction(Mybean bean) {
       // 1. the object allowed into this save action 
       // are all that is available objects withing MyBean. 
       // If it has user define but not telephone. Then
       // if telephone is passed to myAction it will fail and not recognise 
       // field
       // When declaring Date someField or User user then the params now 
       // received as bean this way is now actually properly bound 
       // to the data / domainType declared. 
       // Meaning user will now be actual user or someField actually Date 
      }
    

    所以现在解释如何最好地解决这个问题。创建 bean 时,只需将域文件夹中的实际域类复制到 grails 2 中的 src/groovy/same/package 或 grails 3 中的 src/main/groovy/same/package

    更改名称/类或从Booking 复制为BookingBean,使其具有不同的名称。

    在 grails 2 中的实际 BookingBean 上方添加 @Validateable 或在 grails 3 中将实现添加到主类,如 Class BookingBean implements Validateable {

    现在由于它被复制,所有对象都是相同的,此时来自控制器的保存将是

    class MyController {
    
         def myAction(BookingBean bean) {
             Booking booking = new Booking()
             // this will save all properties
             booking.properties = bean
             booking.save()
         }
    } 
    

    但是你有一个特殊的情况,你想在主域类中声明一个瞬态字段,我会做的是

    class BookingBean {
      def id
      String contactFirstname
      String contactLastname
      boolean secondSave=false
    
     static constraints = {
         id(nullable: true, bindable: true)
        contactFirstname(nullable:true) //,validator:checkHasValue)
        contactLastname(nullable:true) //,validator:checkHasValue)
        secondSave(nullable:true,validator:checkHasValue))
    
    }
    
    //use the same validator since it is doing identical check
    
    static checkHasValue={value,obj,errors->
        // So if secondSave has a value but contactFirstName 
        // is null then complain about contactFirstName
        // you can see how secondSave gets initialise below
        //typical set this to true when you are about to save on 2nd attempt
        //then when set run validate() which will hit this block below
    
        // Check all the things you think should have a 
        // value and reject each field that don't
        if (val) {
            if ( !obj.contactFirstname) {
                errors.rejectValue('contactFirstname',"invalid.contactFirstname")
            }
            if ( !obj.contactSecondname) {
                errors.rejectValue('contactSecondname',"invalid.contactSecondname")
            }
            //and so on
        }
    }
    

    所以现在在你的控制器中:

    class MyController {
    
         def save1(BookingBean bean) {
             Booking booking = new Booking()
             // this will save all properties
             booking.whatEver = bean.whatEver
             booking.save()
    
    
     // you can choose to validate or not here
     // since at this point the secondSave has 
     // not been set therefore validation not called as yet in the bean
    
         }
    

    //你可能有 id,它应该与实际的域类绑定

    def save2(BookingBean bean) {
    
             booking.secondSave=true
             if (!bean.validate()) {
                //this is your errors 
                //bean.errors.allErrors
                return
             }
             //otherwise out of that loop since it hasn't returned
             //manually set each object 
             booking.contactFirstname=bean.contactFirstName
             booking.contactSecondname=bean.contactSecondname
             booking.save()
    
    
         }
    

    }

    e2a 旁注 - 上面应该回答

    在你创建它之前不要验证它。仅在创建对象然后添加值后对其进行验证。替代方法可能在您作为第二次检查的一部分运行的验证 bean 中创建一个函数。 This Example bean 未验证 until formatRequest 被称为 as seen here

    【讨论】:

    • 域模型具有可为空的真值,因为我现在创建对象并将其持久化。切换到另一个页面后,我想对某些字段进行验证,因此想在之后验证这些字段。我可以通过 validate() 传递一些参数以便在我请求时进行额外检查吗?
    • 如上所述,该字段可以为空,并且只有当它具有 val 或值时,它才会尝试验证该值。因此,简而言之,您可以保存它,因为它目前正在保存而没有任何价值。当它获得一个值时,它将尝试根据 checkHasValue 验证集验证它,因此 if (val && val.length()
    • 这听起来合乎逻辑,但是:如果提供了某些值,则不应激活验证,而是在创建对象之后的处理步骤中:1. 创建对象,2.显示表单和验证。您的验证器仅在传递值且与长度不匹配时才会触发。您将如何创建它?
    • 世界是你的牡蛎你选择如何/何时验证我更新了asnwer
    • 世界是你的牡蛎你选择如何/何时验证我更新了asnwer。提供的考试捕获 try catch 中的错误 - 不会封装您可以使用 booking.errors.allErrors.collect{g.message(error : it)} 来收集所有错误(如果它没有被验证为基本错误)示例
    【解决方案2】:

    我不了解您的问题的具体细节,因此我将提供一些一般性指导,因为我刚刚对此进行了深入研究。

    1. 不要在 validate() 之前调用 hasErrors()。如果你这样做了,Grails 不会给你来自域约束的错误,你最终只会遇到你使用 rejectValue() 设置的错误。

    2. 小心使用rejectValue()。尝试使用域约束设置所有错误。如果您有复杂的约束,请使用验证器语法,并且 obj.getPersistentValue() 可能偶尔会成为您的朋友。

    3. 如果您仍然必须使用rejectValue(),请了解以后对validate() 的任何调用都将从头开始并清除您之前的错误。我已经为此编写了一个解决方法(放置在您的域对象中),尽管我不能向您保证它是 100% 好的:

    def validateWithErrors(def fields = null) { def existingErrors = this.errors def ret = (fields ? this.validate(fields) : this.validate()) existingErrors?.allErrors?.each { error -> this.errors.rejectValue(error.field, error.code) } return (existingErrors?.allErrors ? false : ret) }

    【讨论】:

    • 感谢您的反馈,我完全同意#1 和#3。 #2 我部分同意,请参阅第 2 号帖子的评论,我认为这不能通过域模型上的验证器来解决,因为我想在保存对象后进行验证。
    猜你喜欢
    • 1970-01-01
    • 2014-08-07
    • 2012-03-31
    • 2012-06-15
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多