Grails - Простая проблема hasMany - Использование флажков вместо HTML Выберите в create.gsp - PullRequest
7 голосов
/ 11 мая 2010

Моя проблема заключается в следующем: я хочу создать экземпляр домена grails, определив множество экземпляров другого домена, который у него есть. У меня есть фактический источник в Проекте Google Code , но следующая проблема должна проиллюстрировать эту проблему.

class Person {
  String name
  static hasMany[skills:Skill]

  static constraints = {
   id (visible:false)   
   skills (nullable:false, blank:false)
  }
}

class Skill {
  String name
  String description

  static constraints = {
   id (visible:false)   
   name (nullable:false, blank:false)
   description (nullable:false, blank:false)
  }
}

Если вы используете эту модель и def scaffold для двух контроллеров, то вы получите такую ​​форму, которая не работает;

Scaffolding

Моя собственная попытка заставить это работать, перечисляет Навыки как флажки и выглядит следующим образом;

Custom Create.gsp

Но когда я спасаю Волонтера, навыки становятся нулевыми!

Failed to save Skills

Это код моего метода сохранения;

def save = {
    log.info "Saving: " + params.toString()
    def skills = params.skills
    log.info "Skills: " + skills 
    def volunteerInstance = new Volunteer(params)
    log.info volunteerInstance
    if (volunteerInstance.save(flush: true)) {
        flash.message = "${message(code: 'default.created.message', args: [message(code: 'volunteer.label', default: 'Volunteer'), volunteerInstance.id])}"
        redirect(action: "show", id: volunteerInstance.id)
        log.info volunteerInstance
    }
    else {
        render(view: "create", model: [volunteerInstance: volunteerInstance])
    }
}

Это мой вывод журнала (у меня есть собственные методы toString ());

2010-05-10 21:06:41,494 [http-8080-3] INFO  bumbumtrain.VolunteerController  - Saving: ["skills":["1", "2"], "name":"Ian", "_skills":["", ""], "create":"Create", "action":"save", "controller":"volunteer"]

2010-05-10 21:06:41,495 [http-8080-3] INFO  bumbumtrain.VolunteerController  - Skills: [1, 2]

2010-05-10 21:06:41,508 [http-8080-3] INFO  bumbumtrain.VolunteerController  - Volunteer[ id: null | Name: Ian | Skills [Skill[ id: 1 | Name: Carpenter ] , Skill[ id: 2 | Name: Sound Engineer ] ]] 

Обратите внимание, что в последней строке журнала правильные навыки были подобраны и являются частью экземпляра объекта. Когда волонтер сохраняется, «Навыки» игнорируются и не передаются в базу данных, несмотря на то, что созданная в памяти версия явно содержит элементы. Разве невозможно пройти Навыки во время строительства? Должен быть способ обойти это? Мне нужна одна форма, чтобы позволить человеку зарегистрироваться, но я хочу нормализовать данные, чтобы я мог добавить больше навыков позже.

Если вы думаете, что это должно "просто работать", тогда ссылка на рабочий пример была бы отличной.

Если я использую HTML Select, тогда он работает нормально! Например, чтобы создать страницу Create *; 1034 *

<tr class="prop">
<td valign="top" class="name">
  <label for="skills"><g:message code="volunteer.skills.label" default="Skills" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: volunteerInstance, field: 'skills', 'errors')}">
    <g:select name="skills" from="${uk.co.bumbumtrain.Skill.list()}" multiple="yes" optionKey="id" size="5" value="${volunteerInstance?.skills}" />
</td>
</tr>   

Но мне нужно, чтобы он работал с такими флажками ;

<tr class="prop">
<td valign="top" class="name">
  <label for="skills"><g:message code="volunteer.skills.label" default="Skills" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: volunteerInstance, field: 'skills', 'errors')}">
    <g:each in="${skillInstanceList}" status="i" var="skillInstance">   
      <label for="${skillInstance?.name}"><g:message code="${skillInstance?.name}.label" default="${skillInstance?.name}" /></label>
                                      <g:checkBox name="skills" value="${skillInstance?.id.toString()}"/>
    </g:each>
</td>
</tr> 

Вывод журнала абсолютно одинаков! При использовании обоих стилей формы экземпляр Volunteer создается с навыками, правильно указанными в переменной «Skills». При сохранении последнее завершается ошибкой с исключением нулевой ссылки, как показано в верхней части этого вопроса.

Надеюсь, это имеет смысл, заранее спасибо!

Гав

Ответы [ 4 ]

5 голосов
/ 11 мая 2010

Замените ваш create.gsp <g:checkbox...> код на:

<g:checkBox name="skill_${skillInstance.id}"/>

Затем в действии save вашего контроллера замените def volunteerInstance = new Volunteer(params) на:

def volunteerInstance = new Volunteer(name: params.name)
params.each {
  if (it.key.startsWith("skill_"))
    volunteerInstance.skills << Skill.get((it.key - "skill_") as Integer)
}

Должно работать. (код не проверен)

3 голосов
/ 13 января 2013

Grails не обеспечивает поддержку привязки данных, когда вы используете флажок и хотите связать ToMany связей. По крайней мере, до версии 2.2.0

Обходной путь?

1º опция - написать код gsp, который ведет себя как выбранный компонент

<g:each var="skillInstance" in="${skillInstanceList}">
    <div class="fieldcontain">
        <g:set var="checked" value=""/>
        <g:if test="${volunteerInstance?.skills?.contains(skillInstance)}">
            <input type="hidden" name="_skills" value="${skillInstance?.id}"/> 
            <g:set var="checked" value="checked"/>
        </g:if>
        <label for="${skillInstance?.name}">
            <g:message code="${skillInstance?.name}.label"
                       default="${skillInstance?.name}" />
        </label>
        <input type="checkbox" name="skills" value="${skillInstance?.id}"
               ${checked} /> 
    </div>
</g:each>

Создайте свой собственный TagLib

/**
  * Custom TagLib must end up with the TagLib suffix
  *
  * It should be placed in the grails-app/taglib directory
  */
class BindingAwareCheckboxTagLib {

    def bindingAwareCheckbox = { attrs, body ->
        out << render(
                  template: "/<TEMPLATE_DIR>/bindingAwareCheckboxTemplate.gsp",
                  model: [referenceColletion: attrs.referenceColletion,
                          value:attrs.value])
    }

}

Где должен быть относительно каталога /grails-app/views. Кроме того, шаблоны должны иметь префикс _.

Теперь вы можете использовать свой собственный TagLib следующим образом

<g:bindingAwareCheckbox
      referenceCollection="${skillInstanceList}"
      value="${volunteerInstance?.skills}"/>

Как только процесс привязки будет выполнен, автоматически . Дополнительный код не требуется.

3 голосов
/ 29 ноября 2011

Я хотел бы, чтобы читатель отправил список идентификаторов вашего, который имеет много элементов, потому что это можно легко назначить по умолчанию в Grails. Ваш .gsp должен выглядеть так:

<g:each in="${skills}" var="skill">
            <input type="checkbox"
                   name="skills"
                   value="${skill?.id}"
          </g:each>

и в вашем контроллере вы можете просто сохранить значение следующим образом:

person.properties = params
person.validate()
person.save()

Это довольно легко, не так ли? : -)

0 голосов
/ 13 марта 2013

GSP

 <g:checkBox name="skills" value="${skillInstance.id}" checked="${skillInstance in volunteerInstance?.skills}"/>

Groovy

def volunteerInstance = new Volunteer(params).save()     
def skills = Skill.getAll(params.list('skills')) 
     skills.each{ volunteerInstance.addToSkills(it).save() }
...