7

私の問題は次のとおりです。grails ドメイン インスタンスを作成し、それが持つ別のドメインの「多くの」インスタンスを定義したいと考えています。Google Code Projectに実際のソースがありますが、以下で問題を説明する必要があります。

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 scaffold2 つのコントローラーで使用すると、このようなフォームは機能しません。

足場

これを機能させるための私自身の試みは、スキルをチェックボックスとして列挙し、次のように表示します。

カスタム Create.gsp

しかし、ボランティアを救うと、スキルが無効になります!

スキルの保存に失敗しました

これは私の save メソッドのコードです。

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 を使用すると、正常に動作します。次のような作成ページを作成します。

<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' 変数で正しく参照されているスキルで作成されます。保存すると、この質問の上部に示されているように、後者は null 参照例外で失敗します。

これが理にかなっていることを願っています、事前に感謝します!

ガブ

4

4 に答える 4

5

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)
}

動作するはずです。(コードはテストされていません)

于 2010-05-10T23:36:37.327 に答える
3

チェックボックスを使用してToManyアソシエーションをバインドする場合、Grails はデータ バインディングのサポートを提供しません。少なくとも、バージョン 2.2.0 まで

回避策 ?

1º オプション- select コンポーネントのように振る舞う 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])
    }

}

<TEMPLATE_DIR> は、/grails-app/viewsディレクトリに対して相対的である必要があります。さらに、テンプレートには接頭辞を付ける必要があり_ます。

カスタム TagLib を次のように使用できるようになりました。

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

完了すると、バインド プロセスが自動的に行われます。追加のコードは必要ありません。

于 2013-01-12T22:47:01.530 に答える
3

これはGrailsでデフォルトで簡単に割り当てることができるため、多くの要素があるIDリストを読者に送信します。.gsp は次のようになります。

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

コントローラーでは、次のように値を簡単に保存できます。

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

とても簡単ですね。:-)

于 2011-11-29T16:51:45.813 に答える
0

GSP

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

グルーヴィー

def volunteerInstance = new Volunteer(params).save()     
def skills = Skill.getAll(params.list('skills')) 
     skills.each{ volunteerInstance.addToSkills(it).save() }
于 2013-03-13T12:06:58.763 に答える