Nix
Nix

Reputation: 58622

how to manage a one to many relationship - grails

I have a simple problem. I need to manage a hasMany collection on a domain object. I thought I was doing it correct, but it didn't work. I found another post but it is very dated and does not work (Handling parameters from dynamic form for one-to-many relationships in grails)

class User{
   static hasMany = ['prefs': Preference] 
}
class Preference{
  Boolean email
  Boolean site
  Date dateCreated
  Date lastUpdated

}

GSP

<g:each var="t" in="${user.prefs}" status="idx">            
  <li>
    <input type='hidden' name="prefs[${idx}].id" value="${t.id}"/>
    Email: <g:checkBox name="prefs[${idx}].email" value="${t.email}" />
    Site: <g:checkBox name="prefs[${idx}].site" value="${t.site}" /><
  </li>
</g:each>

Controller:

log.info(user.prefs)
user.properties = params
if(!user.save()){ ... }

It then errors out:

UserController - [Preference : 3, Preference : 4]

Error 2013-06-04 21:54:41,405 [http-bio-8080-exec-12] ERROR errors.GrailsExceptionResolver

IndexOutOfBoundsException occurred when processing request: [POST] /user/prefs - parameters: prefs[0].email:
id: 2
prefs[1].site: on
prefs[0].email: on
_prefs[1].site:
_prefs[1].email:
prefs[1].id: 3
_prefs[0].site:
prefs[0].site: on
prefs[0].id: 4
Index: 1, Size: 1. Stacktrace follows:
Message: Index: 1, Size: 1


Upvotes: 0

Views: 275

Answers (2)

Nix
Nix

Reputation: 58622

So I ended up fixing this and it was a hybrid approach. @Meam was correct, it was an ordering issue, but instead of worrying about ordering I just used @dmahapatro 's approach and set Preference to be a list. A side note on this is the definition of List preference has to come before the static hasMany definition. Or you will get a random error when trying to create User. Lastly, when you originally setup the relationship you have to use addTo to link the two...

class User{
  List preference
  static hasMany = ['prefs': Preference] 
}

//another thing I did not know in order to originaly link 
//the two when using lists, you have to use addTo...
user.addToPrefs(
    new Preference(email: true, site:false)
)

The last thing I wanted to mention is there is a bug in <g:checkbox> if you use it with a hasMany like I did it will not work if you try to uncheck the value. I was able to work around it via coping the code from github for FormTagLib. And then updated the code with the other post i was reading https://stackoverflow.com/a/2942667/256793 that has a solution but it was a little dated. So here is the updated version:

 //old code
 //out  << "<input type=\"hidden\" name=\"_${name}\""
 //new...
 def begin =  name.lastIndexOf('.') +1
 def tail =  name.substring( begin);
 out << "<input type=\"hidden\" name=\"${name.replace(  tail, "_" + tail  )}\" "

Upvotes: 0

Meam
Meam

Reputation: 266

I found this problem few days ago. And spent 2 days to fix it.

You have to make sure that children index in params is ordered same as parent's object. In this case, the children's order is [Preference: 3, Preference: 4]. But the params order is prefs[0].id = 4, prefs[1].id = 3. The order is different.

I have to reorder the children index in params before bind them.

Upvotes: 1

Related Questions