Rendering command validation errors across a redirect
Asked Answered
W

2

7

I cannot render the errors from my command object. It does the job well but my .gsp view does not render the errors I raise.

Here is my controller action:

def handleModifyProfile2 = { CreditProviderModificationCommand cpmc -> // bind params to the command object
   if (cpmc.hasErrors()) {
      flash.message = "Error modifying your profile:"
      redirect(action: "modifyProfile", params: [creditProvider : cpmc])
   } ...

Here is how I try to render the errors in my .gsp view:

<g:hasErrors bean="${creditProvider}">
   <div class="errors">
       <g:renderErrors bean="${creditProvider}" as="list" />
   </div>
</g:hasErrors>

How can I get the errors to be displayed in the view?

Womanhood answered 16/8, 2011 at 14:35 Comment(0)
A
9

You can't send the command across in a redirect using params. You have a couple options:

  • render() in the error condition instead of redirect()ing:

    if(cpmc.hasErrors()) {
        render(view: 'profile', model: [creditProvider: cpmc])
    }
    

    This is the most common idiom for what you're doing.

  • Store the command in the session to persist it across the redirect:

    if(cpmc.hasErrors()) {
        session.cpmc = cpmc
        redirect(...)
    }
    
    // and in your action
    def cpmc = session.cpmc ?: null
    render(view: 'profile', model: [creditProvider: cpmc])
    

    This option is somewhat questionable. If not done correctly, you can pollute the session and leave objects hanging around, taking up memory. If done correctly, though, it can be a decent way to implement a post-redirect-get.

Angy answered 16/8, 2011 at 14:47 Comment(5)
AH ah ! You have ended up 3 hours of roaming. Thank you very much !Womanhood
Thanks, I found your answer helpful too. BTW, I suppose using flash instead of session directly would prevent session-pollution?Anatto
@Anatto Yeah, the flash scope should clean up after itself. I haven't looked to see what it's doing behind the scenes, but I'm guessing it's just using the session for storage. It's probably good for something like this.Angy
Yeah, using the flash scope to display errors is another common idiom. Not necessarily the best idiom, though. See also grails.org/doc/latest/ref/Controllers/chain.html to redirect with a model.Mani
Hah, I just noticed that chain uses flash scope.Mani
T
0

With Grails 3 (I don't know if this worked earlier) it's possible to use the flash for this. According to the documentation, the flash will be "cleared at the end of the next request".

I like to use a pattern like this:

def save(MyDomain myDomain) {
    if (myDomain.validate()) {
        myDomain.save()
    } else {
        flash.errors = myDomain.errors
    }
    redirect(action: 'edit', id: myDomain.id)
}

def edit(MyDomain myDomain) {
    if (flash.errors) {
        myDomain.errors = (Errors) flash.errors
    }
    return [myDomain: myDomain]
}

I don't like to use render() for this kind of error handling, because it makes URLs shown in the browser inconsistent with the shown page. This breaks when users set bookmarks, for example.

Tatum answered 17/5, 2019 at 14:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.