Индексируемые параметры Грааля
У меня есть список Team
объекты, которые имеют Integer seed
имущество. Я хочу редактировать семена всех команд сразу, в одной форме. Я уверен, что Grails поддерживает индексированные параметры, но я не могу заставить его работать.
Вот что у меня есть, и это работает, но я прыгаю через слишком много обручей, и должен быть лучший способ.
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>
контроллер:
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)
}
Разве это не ужасно? Слишком много шума. Как я могу сделать что-то вроде:
params.teams.each { t ->
def team = Team.get(t.id)
team.seed = t.seed
}
То есть, как мне сопоставить параметры с именем team[0].seed
, team[0].id
, team[1].seed
, team[1].id
в список?
В Stripes вы можете просто иметь List<Team>
собственности, и это будет просто работать. Я ожидаю не меньше от Грааля!;-)
Заранее спасибо за помощь.
4 ответа
Я наконец-то понял, как это сделать без всяких махинаций.
Забудьте о скрытом параметре и просто используйте идентификатор команды в параметре seed. В ГСП:
<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>
Затем в контроллере:
params.teams.seeds.each { teamId, seed ->
def team = Team.get(teamId.toInteger())
team.seed = seed.toInteger()
team.save()
}
redirect(action:list)
Работает как шарм.
params
это больше, чем обычная карта, это GrailsParameterMap
это автоматически создает структуры Sub-Map на основе разделения имен параметров на ".". Вы можете воспользоваться этим, используя следующий 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: в атрибутах имени нет []. Контроллер теперь довольно прост с использованием магии черного Грааля:
log.error "params = ${params}"
params.teams.findAll {k,v -> !k.contains(".")}.each { k,v ->
bindData(Team.get(v.id), v)
}
Первая операция findAll
отфильтровывает все параметры с точкой внутри. Остальное - карта карт с идентификатором строки в k
и id
а также seed
в v
,
Надеюсь, это ответит на ваш вопрос.
В 2015 году....
Grails теперь работает немного по-другому, и вы можете столкнуться со строками, а не с ожидаемыми вложенными картами.
что-то вроде..
params.nested.each{
if(!it.getKey().contains('.')){
//to get a map rather than a string...
params.nested[it.getKey()];
}
};
РЕДАКТИРОВАТЬ: Кстати...
входы с одинаковыми именами, например
<input name="item.choice" type="checkbox" value="3" />
< input name="item.choice" type="checkbox" value="4"/>
Помещаются в список ЕСЛИ более одного. Так что, если оба из вышеупомянутых были проверены
<input name="item.choice" type="checkbox" value="3" checked />
< input name="item.choice" type="checkbox" value="4" checked/>
Вы бы получили список.
Но если проверен только один, вы НЕ получите Список (по крайней мере, в версии Grails, которую я использую), вы получите одно значение.
<input name="item.choice" type="checkbox" value="3" checked />
< input name="item.choice" type="checkbox" value="4" />
Это означает, что в контроллере, если бы я сделал что-то вроде
params['item.choice'].each{
def item=Item.get(it)
}
Будет выдано сообщение об ошибке, если будет отправлен только один элемент. Один отличный способ обойти это
([]+(params['item.choice']?:[])).each{
def item=Item.get(it)
}
Если параметр установлен, а не список, он помещает значение в пустой список; Если задан параметр и список, оператор плюс добавит все отдельные значения в пустой список; если параметр не задан, он добавит два пустых списка вместе, что создаст один пустой список.
Не уверен, что это поможет, но вы можете использовать закрытие с ним, как:
<g:each in="${teams}">
<p>id: ${it.id}</p>
<p>seed: ${it.seed}</p>
</g:each>
Вы могли бы, вероятно, построить список из этого набора вывода HTML. или создайте свою форму с этим.