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?

Burt Beckwith
  • 75,342
  • 5
  • 143
  • 156
Alexandre Bourlier
  • 3,972
  • 4
  • 44
  • 76

2 Answers2

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.

Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
  • AH ah ! You have ended up 3 hours of roaming. Thank you very much ! – Alexandre Bourlier Aug 16 '11 at 15:13
  • 1
    Thanks, I found your answer helpful too. BTW, I suppose using flash instead of session directly would prevent session-pollution? – tim_wonil Oct 05 '11 at 05:26
  • @tim_wonil 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. – Rob Hruska Oct 05 '11 at 12:25
  • 1
    Yeah, using the flash scope to display errors is another common idiom. Not necessarily the best idiom, though. See also http://grails.org/doc/latest/ref/Controllers/chain.html to redirect with a model. – Charles Wood Jul 15 '14 at 21:51
  • 1
    Hah, I just noticed that `chain` uses flash scope. – Charles Wood Sep 03 '14 at 15:44
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.