Grails indexed parameters

2019-02-02 03:21发布

问题:

I have a list of Team objects that have an Integer seed property. I want to edit all the teams' seeds at once, in a single form. I'm sure that Grails supports indexed parameters, but I can't get it to work.

Here is what I have, and it works but I'm jumping through way too many hoops and there's gotta be a better way.

gsp:

<g:form action="setSeeds">
...
  <g:each in="${teams}" status="i" var="team">
    <input type="hidden" name="teams[${i}].id" value="${team.id}">
    <input type="text" size="2" name="teams[${i}].seed" value="${team.seed}">
  </g:each>
</g:form>

controller:

def setSeeds = {
  (0..<30).each { i ->
    def team = Team.get(Integer.parseInt(params["teams[${i}].id"]))
    team.seed = Integer.parseInt(params["teams[${i}].seed"])
  }
  redirect(action:list)
}

Isn't that awful? Way too much noise. How can I do something along the lines of:

params.teams.each { t ->
  def team = Team.get(t.id)
  team.seed = t.seed
}

That is, how do I map params named team[0].seed, team[0].id, team[1].seed, team[1].id to a List?

In Stripes you can just have a List<Team> property and it will just work. I expect no less from Grails! ;-)

Thanks in advance for your help.

回答1:

params is more than a regular Map, it's a GrailsParameterMap that automatically builds up sub-Map structures based on splitting the parameter names by '.'. You might take advantage of this by using the following gsp:

<g:form action="seed">
 <g:each in="${teams}" status="i" var="team">
   <input type="hidden" name="teams.${i}.id" value="${team.id}">
   <input type="text" size="2" name="teams.${i}.seed" value="${team.seed}">
  </g:each>
 <g:submitButton name="update" value="Update" />
</g:form>

NB: there's no [] in the name attributes. The controller is now pretty simple using some black Grails magic :

log.error "params = ${params}"
params.teams.findAll {k,v -> !k.contains(".")}.each { k,v ->
       bindData(Team.get(v.id), v)
}

The first operation findAll filters out all parameters with a dot inside. The rest is a map of maps holding the row id in k and the id and seed in v.

I hope this answers your question.



回答2:

I finally figured out how to do this without any shenanigans.

Forget about the hidden parameter and just use the team ID in the seed parameter. In the GSP:

<g:form action="setSeeds"> 
... 
  <g:each in="${teams}" var="team"> 
    <input type="text" size="2" name="teams.seeds.${team.id}"
      value="${team.seed}"> 
  </g:each> 
</g:form> 

Then, in the controller:

params.teams.seeds.each { teamId, seed ->
  def team = Team.get(teamId.toInteger())
  team.seed = seed.toInteger()
  team.save()
}
redirect(action:list)

Works like a charm.



回答3:

In 2015.... Grails works a little differently now and you may find yourself running into strings rather than the expected sub-maps.I got something to work by doing
something like..

params.nested.each{
               if(!it.getKey().contains('.')){
                 //to get a map rather than a string...    
                  params.nested[it.getKey()];
               }
           };

EDIT: By The Way...

inputs that have the same name, like

  <input name="item.choice" type="checkbox" value="3" />
  < input name="item.choice" type="checkbox" value="4"/>

Are put into a List IF more then one are submitted. So if both of the above were checked

   <input name="item.choice" type="checkbox" value="3" checked />
   < input name="item.choice" type="checkbox" value="4" checked/>

You would get a list.

But if only one is checked you do NOT get a List(at least in grails verison I use), you get a single value.

     <input name="item.choice" type="checkbox" value="3" checked />
     < input name="item.choice" type="checkbox" value="4" />

That means in a controller, if I were to do something like

  params['item.choice'].each{
          def item=Item.get(it)
  }

It would throw an error if only one item was submitted. One groovy way to get around this is

 ([]+(params['item.choice']?:[])).each{
         def item=Item.get(it)
 } 

If the parameter is set and not a list, it puts the value into the empty list; If the parameter is set and a list, the plus operator will add all the individual values to the empty list; if the parameter is not set, it will add two empty lists together, which creates a single empty list.



回答4:

Not sure if this helps but you could use a closure with it like:

<g:each in="${teams}">
     <p>id: ${it.id}</p>
     <p>seed: ${it.seed}</p>
</g:each>

You could probably build a list from this instaed of outputting html. or build your form with it.