欢迎进入Wiki » FAQ » 使用动态表功能将一个hasMany属性在页面上分类显示?

使用动态表功能将一个hasMany属性在页面上分类显示?

在2014-06-17 17:23上被李小翔修改
评论 (0) · 附件 (2) · 记录 · 信息

考虑下面的需求:在HR系统中,需要录入员工的家庭成员信息,而家庭成员分为配偶、子女、其他三大类,配偶需要填写较多的信息,而子女、其他类只需要填写较少的信息,如下图所示。

需求表格.png

将家庭成员设计为一个Domain类,使用 relationship(关系)来区分其类别,Domain类如下所示:

/**
 * 员工
 */

class Employee {

    String name

   /** 家庭成员 */
   static hasMany = [familyMembers: FamilyMember]

}

/**
 * 家庭成员
 */

class FamilyMember {

   /** 员工 */
    Employee employee
   static belongsTo = [employee: Employee]
   
   /** 关系:配偶、子女、其他(父亲、母亲...) */
    String relationship
   public static final String REL_MATE = "mate"            // 配偶
   public static final String REL_CHILDREN = "children"    // 子女
   
   /** 姓名 */
    String name
   /** 出生日期 */
    Date birthdate
   /** 民族 */
    String nation
   /** 户籍 */
    String hukou
   /** 政治面貌 */
    String politicalStatus
   /** 工作时间 */
    Date workingDate
   /** 学历 */
    String education
   /** 联系电话 */
    String phone
   /** 毕业院校及专业 */
    String school
   /** 工作单位 */
    String company
   /** 职务 */
    String position
   
   /** 性别 */
    String gender

}

视图(或控制器)中,先根据 relationship 属性将所有家庭成员分类为配偶、子女和其他:

FamilyMember mate = employeeInstance.familyMembers.find{it.relationship == FamilyMember.REL_MATE}  // 配偶
List<FamilyMember> children = employeeInstance.familyMembers.findAll{it.relationship == FamilyMember.REL_CHILDREN}.toList() // 子女
List<FamilyMember> others = (employeeInstance.familyMembers - children - mate).toList() // 其他

然后,视图开发时,只需要按照下面的步骤处理,即可使用动态表功能实现分类显示与自动保存:

  • 将视图分为三段:配偶、子女、其他,每段显示和处理不同的数据。如配偶段处理对象 FamilyMamber mate,子女段处理列表 children、其他段处理列表 others
  • 只有子女和其他两类使用了动态表标签 g:dynamicTable,其前缀均设为 fm(通过checkboxField属性),各个字段均以 fm 开头,如 fm.name、fm.gender 表示家庭成员的名称、性别;
    • 而配偶段同样采用 fm 前缀来命名HTML元素,以便后台自动解析、合并
    • 两个动态表中,为了全选的 checkbox 和 增/删行 的功能运行正常,动态表名称设为不一样,一个为 familyMembersChildren、familyMembersOthers
  • 为了保证提交的顺序、数据对应关系正确,每段数据中都包含全部属性(即便没有使用)的HTML元素:
    • 如配偶段,使用 g:hiddenField 将 fm.relationship 设置为 FamilyMember.REL_MATE
    • 如子女段,将没有使用的 fm.nation、fm.hukou 等字段均设为空

完整的视图代码如下:

<tr class="prop">
   <td class="name" colspan="4">配偶:</td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.name"><g:message code="bropen.erp.hr.employee.FamilyMember.name" default="Name" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.name}">
           <g:hiddenField name="fm.id" value="${mate?.id ?: -1}" />
           <g:hiddenField name="fm.relationship" value="${FamilyMember.REL_MATE}" />
           <g:hiddenField name="fm.gender" value="" />
           <g:textField name="fm.name" value="${mate?.name}" maxlength="${FamilyMember.constraints.name.maxSize}" />
       </g:formField>
   </td>
   <td class="name"><label for="fm.birthdate"><g:message code="bropen.erp.hr.employee.FamilyMember.birthdate" default="Birthdate" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${DateUtils.formatDate(mate?.birthdate)}">
           <g:datePicker2 name="fm.birthdate" value="${birthdate?.name}" />
       </g:formField>
   </td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.nation"><g:message code="bropen.erp.hr.employee.FamilyMember.nation" default="Nation" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.nation}">
           <g:textField name="fm.nation" value="${mate?.nation}" maxlength="${FamilyMember.constraints.nation.maxSize}" />
       </g:formField>
   </td>
   <td class="name"><label for="fm.hukou"><g:message code="bropen.erp.hr.employee.FamilyMember.hukou" default="Hukou" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.hukou}">
           <g:textField name="fm.hukou" value="${mate?.hukou}" maxlength="${FamilyMember.constraints.hukou.maxSize}" />
       </g:formField>
   </td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.politicalStatus"><g:message code="bropen.erp.hr.employee.FamilyMember.politicalStatus" default="Political Status" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.politicalStatus}">
           <g:textField name="fm.politicalStatus" value="${mate?.politicalStatus}" maxlength="${FamilyMember.constraints.politicalStatus.maxSize}" />
       </g:formField>
   </td>
   <td class="name"><label for="fm.workingDate"><g:message code="bropen.erp.hr.employee.FamilyMember.workingDate" default="WorkingDate" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${DateUtils.formatDate(mate?.workingDate)}">
           <g:datePicker2 name="fm.workingDate" value="${mate?.workingDate}" />
       </g:formField>
   </td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.education"><g:message code="bropen.erp.hr.employee.FamilyMember.education" default="Education" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.education}">
           <g:textField name="fm.education" value="${mate?.education}" maxlength="${FamilyMember.constraints.education.maxSize}" />
       </g:formField>
   </td>
   <td class="name"><label for="fm.phone"><g:message code="bropen.erp.hr.employee.FamilyMember.phone" default="Phone" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.phone}">
           <g:textField name="fm.phone" value="${mate?.phone}" maxlength="${FamilyMember.constraints.phone.maxSize}" />
       </g:formField>
   </td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.school"><g:message code="bropen.erp.hr.employee.FamilyMember.school" default="School" />:</label></td>
   <td class="value" colspan="3">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.school}">
           <g:textField name="fm.school" value="${mate?.school}" maxlength="${FamilyMember.constraints.school.maxSize}" class="max" />
       </g:formField>
   </td>
</tr>
<tr class="prop">
   <td class="name"><label for="fm.company"><g:message code="bropen.erp.hr.employee.FamilyMember.company" default="Company" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.company}">
           <g:textField name="fm.company" value="${mate?.company}" maxlength="${FamilyMember.constraints.company.maxSize}" class="max" />
       </g:formField>
   </td>
   <td class="name"><label for="fm.position"><g:message code="bropen.erp.hr.employee.FamilyMember.position" default="Position" />:</label></td>
   <td class="value">
       <g:formField bean="${employeeInstance}" name="familyMembers" value="${mate?.position}">
           <g:textField name="fm.position" value="${mate?.position}" maxlength="${FamilyMember.constraints.position.maxSize}" />
       </g:formField>
   </td>
</tr>

<tr class="prop">
   <td class="name" colspan="4">子女:</td>
</tr>
<tr class="prop">
   <td class="value" colspan="4">
       <g:if test="${isFormFieldEditable(bean: employeeInstance, name: 'familyMembers') || children}">
       <g:dynamicTable name="familyMembersChildren" class="innerTable" border="1" button="top" checkboxField="fm.id"
           childProperty="familyMembers" requiredField="name"
           editable="${isFormFieldEditable(bean: employeeInstance, name: 'familyMembers')}">
       <thead>
           <tr>
               <th width="15" class="editable"><input type="checkbox" onclick="familyMembersChildren.selectAll(this.checked)"/></th>
               <th width="100"><g:message code="bropen.erp.hr.employee.FamilyMember.name" default="Name" /></th>
               <th width="50"><g:message code="bropen.erp.hr.employee.FamilyMember.gender" default="Gender" /></th>
               <th width="100"><g:message code="bropen.erp.hr.employee.FamilyMember.birthdate" default="Birthdate" /></th>
               <th width="300"><g:message code="bropen.erp.hr.employee.FamilyMember.company2" default="Company" /></th>
           </tr>
       </thead>
       <tbody class="template">
           <tr>
               <td class="center">
                   <input name="fm.id" type="checkbox" value="-1" />
                   <input name="fm.relationship" type="hidden" value="${FamilyMember.REL_CHILDREN}" />
                   <input name="fm.nation" type="hidden" value="" />
                   <input name="fm.hukou" type="hidden" value="" />
                   <input name="fm.politicalStatus" type="hidden" value="" />
                   <input name="fm.workingDate" type="hidden" value="" />
                   <input name="fm.education" type="hidden" value="" />
                   <input name="fm.phone" type="hidden" value="" />
                   <input name="fm.school" type="hidden" value="" />
                   <input name="fm.position" type="hidden" value="" />
               </td>
               <td><g:textField name="fm.name" /></td>
               <td><g:select name="fm.gender" from="${FamilyMember.constraints.gender.inList}" valueMessagePrefix="bropen.erp.hr.employee.FamilyMember.gender" noSelection="['':'']" /></td>
               <td><input type="text" name="fm.birthdate" class="date" /></td>
               <td><g:textField name="fm.company" style="width: 300px" /></td>
           </tr>
       </tbody>
       <tbody>
           <g:each in="${CollectionUtils.sort(children, 'asc', 'birthdate')}" var="fm">
           <tr>
               <td class="center editable">
                   <input name="fm.id" type="checkbox" value="${fm.id}"/>
                   <input name="fm.relationship" type="hidden" value="${FamilyMember.REL_CHILDREN}" />
                   <input name="fm.nation" type="hidden" value="" />
                   <input name="fm.hukou" type="hidden" value="" />
                   <input name="fm.politicalStatus" type="hidden" value="" />
                   <input name="fm.workingDate" type="hidden" value="" />
                   <input name="fm.education" type="hidden" value="" />
                   <input name="fm.phone" type="hidden" value="" />
                   <input name="fm.school" type="hidden" value="" />
                   <input name="fm.position" type="hidden" value="" />
               </td>
               <td><g:formField bean="${fm}" name="name">
                   <g:textField name="fm.name" value="${fm.name}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="gender" valueI18n="true">
                   <g:select name="fm.gender" from="${FamilyMember.constraints.gender.inList}" valueMessagePrefix="bropen.erp.hr.employee.FamilyMember.gender" noSelection="['':'']" value="${fm.gender}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="birthdate">
                   <input type="text" name="fm.birthdate" class="date" value="${DateUtils.formatDate(fm.birthdate)}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="company">
                   <g:textField name="fm.company" style="width:300px" value="${fm.company}"/>
               </g:formField></td>
           </tr>
           </g:each>
       <tbody>
       </g:dynamicTable>
       </g:if><g:else></g:else>
   </td>
</tr>


<tr class="prop">
   <td class="name" colspan="4">其他成员:</td>
</tr>
<tr class="prop">
   <td class="value" colspan="4">
       <g:if test="${isFormFieldEditable(bean: employeeInstance, name: 'familyMembers') || others}">
       <g:dynamicTable name="familyMembersOthers" class="innerTable" border="1" button="top" checkboxField="fm.id"
           childProperty="familyMembers" requiredField="name"
           editable="${isFormFieldEditable(bean: employeeInstance, name: 'familyMembers')}">
       <thead>
           <tr>
               <th width="15" class="editable"><input type="checkbox" onclick="familyMembersOthers.selectAll(this.checked)"/></th>
               <th width="50"><g:message code="bropen.erp.hr.employee.FamilyMember.relationship" default="Relationship" /></th>
               <th width="100"><g:message code="bropen.erp.hr.employee.FamilyMember.name" default="Name" /></th>
               <th width="100"><g:message code="bropen.erp.hr.employee.FamilyMember.birthdate" default="Birthdate" /></th>
               <th width="50"><g:message code="bropen.erp.hr.employee.FamilyMember.politicalStatus" default="Political Status" /></th>
               <th width="200"><g:message code="bropen.erp.hr.employee.FamilyMember.company" default="Company" /></th>
               <th width="50"><g:message code="bropen.erp.hr.employee.FamilyMember.position" default="Position" /></th>
           </tr>
       </thead>
       <tbody class="template">
           <tr>
               <td class="center">
                   <input name="fm.id" type="checkbox" value="-1" />
                   <input name="fm.nation" type="hidden" value="" />
                   <input name="fm.hukou" type="hidden" value="" />
                   <input name="fm.workingDate" type="hidden" value="" />
                   <input name="fm.education" type="hidden" value="" />
                   <input name="fm.phone" type="hidden" value="" />
                   <input name="fm.school" type="hidden" value="" />
                   <input name="fm.gender" type="hidden" value="" />
               </td>
               <td><g:textField name="fm.relationship" style="width: 50px" /></td>
               <td><g:textField name="fm.name" /></td>
               <td><input type="text" name="fm.birthdate" class="date" /></td>
               <td><g:textField name="fm.politicalStatus" style="width: 50px" /></td>
               <td><g:textField name="fm.company" style="width: 200px" /></td>
               <td><g:textField name="fm.position" style="width: 50px" /></td>
           </tr>
       </tbody>
       <tbody>
           <g:each in="${CollectionUtils.sort(others, 'asc', 'name')}" var="fm">
           <tr>
               <td class="center editable">
                   <input name="fm.id" type="checkbox" value="${fm.id}"/>
                   <input name="fm.nation" type="hidden" value="" />
                   <input name="fm.hukou" type="hidden" value="" />
                   <input name="fm.workingDate" type="hidden" value="" />
                   <input name="fm.education" type="hidden" value="" />
                   <input name="fm.phone" type="hidden" value="" />
                   <input name="fm.school" type="hidden" value="" />
                   <input name="fm.gender" type="hidden" value="" />
               </td>
               <td><g:formField bean="${fm}" name="relationship">
                   <g:textField name="fm.relationship" style="width: 50px" value="${fm.relationship}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="name">
                   <g:textField name="fm.name" value="${fm.name}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="birthdate">
                   <input type="text" name="fm.birthdate" class="date" value="${DateUtils.formatDate(fm.birthdate)}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="politicalStatus">
                   <g:textField name="fm.politicalStatus" style="width: 50px" value="${fm.politicalStatus}" />
               </g:formField></td>
               <td><g:formField bean="${fm}" name="company">
                   <g:textField name="fm.company" style="width: 200px" value="${fm.company}"/>
               </g:formField></td>
               <td><g:formField bean="${fm}" name="position">
                   <g:textField name="fm.position" style="width: 50px" value="${fm.position}" />
               </g:formField></td>
           </tr>
           </g:each>
       <tbody>
       </g:dynamicTable>
       </g:if><g:else></g:else>
   </td>
</tr>

最终效果如下图所示:

实现效果.png

在2014-06-17 17:18上被李小翔创建

Copyright © 2013 北京博瑞开源软件有限公司
京ICP备12048974号