1

我目前正在从事一个 grails 项目,并试图创建一个一对多的表单。我一直在使用下面链接中的教程开始:

http://omarello.com/2010/08/grails-one-to-many-dynamic-forms/

该解决方案有一个表单,其中包含两个静态数据字段和一个动态字段,允许用户根据需要添加任意数量的变量,然后保存它们。您可以在下面看到此功能的所有各种文件:

Products.Groovy 域类

import org.apache.commons.collections.list.LazyList;
import org.apache.commons.collections.FactoryUtils;

/**
 * Products
 * A domain class describes the data object and it's mapping to the database
 */
class Products {

    String productType
    String product

    List vars = new ArrayList()

    //This represents a product belonging to a single department
    static belongsTo = [dept:Depts]
    static hasMany = [ vars:Dynam ]
    static mapping = {
        vars cascade:"all-delete-orphan"
    }

    def getVarsList() {
        return LazyList.decorate(
              vars,
              FactoryUtils.instantiateFactory(Dynam.class))
    }

    static constraints = {
        productType blank: false
        product blank:false, size: 1..160
    }

}

动态的groovy

class Dynam {

    public enum VarType{
        T("Testimonial"),
        D("Dimentions"),
        N("Networking")

        final String value;

        VarType(String value) {
            this.value = value;
        }

        String toString(){
            value;
        }

        String getKey(){
            name()
        }

        static list() {
            [T, D, N]
        }

    }

    static constraints = {
        index(blank:false, min:0)
        data(blank:false)
        type(blank:false, inList:VarType.list(), minSize:1, maxSize:1)
    }

    int index
    String data
    VarType type
    boolean deleted

    static transients = [ 'deleted' ]

    static belongsTo = [ product:Products ]

    def String toString() {
        return "($index) ${data} - ${type.value}"
    }
}

ProductsController.Groovy

创造功能

def createDynProduct(){
        def productsInstance = new Products()
        productsInstance.properties = params
        //This is used in order to create a new User Object for the current User logged in
        def userObjects = springSecurityService.currentUser

        //This passes the 2 models to the view one being Products and the other a User Department
        [productsInstance: productsInstance, dept: userObjects.dept]

    }

保存功能

def save() {

        def productInfo = "dynam"

        def userObjects = springSecurityService.currentUser

        def dept = Depts.findByName(params.dept.name)

        def product = new Products(product:productInfo, productType:params.productType, dept: dept, vars:params.varsList)

        def _toBeRemoved = product.vars.findAll {!it}

        // if there are vars to be removed
        if (_toBeRemoved) {
            product.vars.removeAll(_toBeRemoved)
         }

        //update my indexes
        product.vars.eachWithIndex(){phn, i ->
            if(phn)
                phn.index = i
        }

        //If the the save is not successful do this
        if (!product.save(flush: true)) {
            render(view: "create", model: [productsInstance: product, dept: userObjects.dept])
            return
        }

        redirect(action: "show", id: product.id)
    }

createDynProduct.gsp

<%@ page import="com.tool.Products" %>
<html>

  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta name='layout' content='springSecurityUI'/>
    <g:set var="entityName" value="${message(code: 'messages.label', default: 'Products')}" />
    <title><g:message code="default.create.label" args="[entityName]" /></title>
</head>

  <body>

        <g:renderErrors bean="${productsInstance}" />


    <g:form action='save' name='ProductForm' >
        <br/>

        <!-- Render the product template (_dynam.gsp) here -->
        <g:render template="dynam" model="['productsInstance':productsInstance]"/>
        <!-- Render the product template (_dynam.gsp) here -->

        <div class="buttons">
          <g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
        </div>
      </g:form>

    <!-- Render the var template (_var.gsp) hidden so we can clone it -->
    <g:render template='var' model="['var':null,'i':'_clone','hidden':true]"/>
    <!-- Render the var template (_var.gsp) hidden so we can clone it -->

  </body>
</html>

_dynam.gsp

<div class="dialog">
  <table>
      <tbody>

      <tr class="prop">
            <td valign="top" class="name">
            <label for="productType">Product Type</label>
            </td>
            <td>
            <g:textField name='productType' bean="${productsInstance}" value="${productsInstance.productType}"
                           size='40'/>
            </td>
        </tr>

        <tr class="prop">
            <td valign="top" class="name">
            <label for="Vars">Vars</label>
            </td>

            <!-- Render the vars template (_vars.gsp) here -->
            <g:render template="vars" model="['productsInstance':productsInstance]" />
            <!-- Render the vars template (_vars.gsp) here -->

        </tr>

        <tr class="prop">
            <td valign="top" class="name">
            <label for="Department">Department</label>
            </td>
            <td>
            <g:textField name='dept.name' readonly="yes" value="${dept.name}" size='40'/>
            </td>
        </tr>
    </tbody>
  </table>
</div>

_vars.gsp

<script type="text/javascript">
    var childCount = ${productsInstance?.vars.size()} + 0;

    function addVar(){
      var clone = $("#var_clone").clone()
      var htmlId = 'varsList['+childCount+'].';
      var varInput = clone.find("input[id$=number]");

      clone.find("input[id$=id]")
             .attr('id',htmlId + 'id')
             .attr('name',htmlId + 'id');
      clone.find("input[id$=deleted]")
              .attr('id',htmlId + 'deleted')
              .attr('name',htmlId + 'deleted');
      clone.find("input[id$=new]")
              .attr('id',htmlId + 'new')
              .attr('name',htmlId + 'new')
              .attr('value', 'true');
      varInput.attr('id',htmlId + 'data')
              .attr('name',htmlId + 'data');
      clone.find("select[id$=type]")
              .attr('id',htmlId + 'type')
              .attr('name',htmlId + 'type');

      clone.attr('id', 'var'+childCount);
      $("#childList").append(clone);
      clone.show();
      varInput.focus();
      childCount++;
    }

    //bind click event on delete buttons using jquery live
    $('.del-var').live('click', function() {
        //find the parent div
        var prnt = $(this).parents(".var-div");
        //find the deleted hidden input
        var delInput = prnt.find("input[id$=deleted]");
        //check if this is still not persisted
        var newValue = prnt.find("input[id$=new]").attr('value');
        //if it is new then i can safely remove from dom
        if(newValue == 'true'){
            prnt.remove();
        }else{
            //set the deletedFlag to true
            delInput.attr('value','true');
            //hide the div
            prnt.hide();
        }
    });

</script>

<div id="childList">
    <g:each var="var" in="${productsInstance.vars}" status="i">

        <!-- Render the var template (_var.gsp) here -->
        <g:render template='var' model="['var':var,'i':i,'hidden':false]"/>
        <!-- Render the var template (_var.gsp) here -->

    </g:each>
</div>
<input type="button" value="Add Var" onclick="addVar();" />

_var.gsp

<div id="var${i}" class="var-div" <g:if test="${hidden}">style="display:none;"</g:if>>
    <g:hiddenField name='varsList[${i}].id' value='${var?.id}'/>
    <g:hiddenField name='varsList[${i}].deleted' value='false'/>
    <g:hiddenField name='varsList[${i}].new' value="${var?.id == null?'true':'false'}"/>

    <g:textField name='varsList[${i}].number' value='${var?.data}' />    
    <g:select name="varsList[${i}].type"
        from="${com.tool.Dynam.VarType.values()}"
        optionKey="key"
        optionValue="value"
        value = "${var?.type?.key}"/>

    <span class="del-var">
        <img src="${resource(dir:'images/skin', file:'icon_delete.png')}" 
            style="vertical-align:middle;"/>
    </span>
</div>

该解决方案部分有效,因此您可以转到产品页面并加载字段,动态字段被添加到视图中,我可以输入数据。但是,当我单击保存时,静态字段的数据会保留,但动态变量不会保存到域中。

我没有收到任何错误,但我相信它与下面的保存功能行有关,尤其是vars:params.varsList

def product = new Products(product:productInfo, productType:params.productType, dept: dept, vars:params.varsList)

我已经使用 println 检查了 varsList 数据,它返回 null,有人可以帮忙吗?

提前致谢

4

0 回答 0