处理动态表单中的参数,用于grails中的一对多关系

时间:2010-08-17 17:09:31

标签: spring grails groovy grails-controller

我的主要问题是在一个动态表单中管理一对多关系时处理pramas映射,以及在编辑/更新域对象时处理一对多的最佳实践动态形式。我的问题的输入如下。

我设法破解了一个允许我在一个动态表单中创建下面显示的域对象的表单,因为没有必要使用单独的表单来创建电话号码然后将它们分配给联系人,这使得在我的应用程序中以一种形式创建所有内容的意义。我设法实现了类似于我在Previous Question中提出的问题(感谢帮助过的人)

class Contact{

    String firstName
    String lastName
    // ....
    // some other properties
    // ...

    static hasMany = [phones:Phone]
    static mapping = {
        phones sort:"index", cascade: "all-delete-orphan"
    }
}

class Phone{
    int index
    String number
    String type
    Contact contact

    static belongsTo = [contact:Contact]
}

我基本上设法从'params'映射中获取值并自己解析它们并手动创建域对象和关联。即我没有使用默认脚手架中使用的相同逻辑,即

Contact c = new Contact(params)

等....,我只是通过所有参数并手工制作我的域对象并保存它们,一切正常。

我的控制器的代码块看起来像这样(这是剥离的,只是为了显示一个点)

//create the contact by handpicking params values
def cntct = new Contact()
cntct.firstName = params.firstName
cntct.lastName = params.lastName
//etc...

//get array of values for number,type
def numbers = params['phone.number']
def types =  params['phone.type']

//loop through one of the arrays and create the phones
numbers.eachWithIndex(){ num, i ->
    //create the phone domain object from 
    def phone = new Phone()
    phone.number = num
    phone.type = types[i]
    phone.index = i
    cntct.addToPhones(phone)
}

//save

我的问题如下:

  • 处理这种情况的最佳做法是什么,在这种情况下使用Command对象是否有效,如果是,我在哪里可以找到更多关于此的信息,我在搜索过程中找到的所有示例都是一对一的关系,我找不到一对多的例子?
  • 在编辑联系人对象时添加/删除手机方面,在这种情况下处理手机相关性的最佳方法是什么?我的意思是创建逻辑很简单,因为我必须始终在保存时创建新手机,但在处理更新联系人时,用户可能已经删除了手机和/或编辑现有手机和/或添加了一些新手机。现在我所做的只是删除联系人拥有的所有手机,并根据表单发布的内容重新创建它们,但我觉得这不是最好的方法,我也不认为现有的那些并与发布的值进行比较并进行手动差异也是最好的方法,是否有最佳实践如何处理?

谢谢,希望问题很明确。

[edit]只是为了获取更多信息,可以使用表格中的javascript(jquery)动态添加和删除手机信息[/ edit]

4 个答案:

答案 0 :(得分:1)

免责声明:我不知道使用grails时是否有以下方法。我稍后再说。

better way for dynamic forms。作者说:

  

要添加LineItems,我有一些js计算新索引并将其添加到DOM。删除LineItem 我必须重新编号所有索引,这是我想避免的

所以我做什么

我有一个存储下一个索引的变量

var nextIndex = 0;

当页面加载时,我执行一个JavaScript函数,它计算集合中有多少子集并配置nextIndex变量。您可以使用JQuery或YUI,随意。

静态添加子项

我创建了一个存储模板的变量(注意 {index}

var child   = "<div>"
           +=     "<div>"
           +=         "<label>Name</label>"
           +=         "<input type="text" name=\"childList[{index}].name\"/>"
           +=     "</div>"
           += "</div>"

当用户点击添加孩子按钮时,我使用正则表达式替换 {index} - 通过存储在nextIndex变量中的值并递增1。然后我添加到DOM

另见Add and Remove HTML elements dynamically with Javascript

以动态添加儿童

Here你可以看到The Paolo Bergantino解决方案

删除

但我认为删除时问题就会成长。无论您删除了多少个孩子,都不会触及nextIndex变量。见这里

/**
  * var nextIndex = 3;
  */

<input type="text" name="childList[0].name"/>
<input type="text" name="childList[1].name"/> // It will be removed
<input type="text" name="childList[2].name"/>

假设我删除 childList 1 我做什么???我应该重新编号所有索引???

在服务器端,我使用AutoPopulatingList。由于已删除了childList 1,因此AutoPopulatingList 将其处理为null 。所以在初始化时我做了

List<Child> childList = new AutoPopulatingList(new ElementFactory() {
   public Object createElement(int index) throws ElementInstantiationException {
       /**
         * remove any null value added
         */    
       childList.removeAll(Collections.singletonList(null));

       return new Child();
   }
});

这样,我的集合只包含两个子节点(没有任何空值)和我不需要重新编号客户端的所有索引

关于添加/删除您可以看到这个link,其中我展示了一个可以为您提供一些见解的场景。

另见Grails UI plugin

答案 1 :(得分:1)

谢谢,

您的回答为我提供了一些更深入的搜索,我实际上发现了一个很好的帖子,涵盖了我的问题中的所有输入。这只是阅读此内容的人的参考。我将写一篇关于我如何尽快实现我的案例的博客文章,但是这个链接应该提供一个很好的ino来源于一个有效的工作。

http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/

答案 2 :(得分:0)

大多数时候我使用ajax来管理这样的问题。

因此,当用户点击add new phone时,我从服务器获取模板UI以实现可管理性(UI只是我用来编辑的GSP模板,更新手机),所以这样你就不会混合你的使用您的js代码的UI,每当您想要更改UI时,您只需要处理我们的GSP代码。

然后在获取UI后,我使用jquery DOM操作将其添加到页面中。然后在填写表单后,当他们点击add(save)时,请求将通过ajax发送到服务器并立即保留。

当用户点击edit phone时,从装有现有手机数据的服务器加载相同的UI模板,然后点击更新将立即通过ajax更新相应的手机,同样的事情适用于删除操作。

但是有一天我得到了一个用例的另一个场景,说“直到我说保存联系人没有电话应该保存在后端,也是在ui上的联系人添加电话后如果导航到另一页然后回到联系页面我之前添加的手机必须还在那里。“呃..

为此,我开始使用Session,因此我解释的上述操作将作用于我存储在会话而不是数据库中的电话列表对象。这很简单,只需执行phonesInSession上的所有操作,但最后不要忘记这样做(删除更新):

phonesToBeDeleted = phonesInDB - phonesInSession 

phonesToBeDeleted.each{
contact.removeFromPhones(it)
it.delete()
}

我知道我不需要在会话中放入大量数据,但这是我为我的场景获得的唯一解决方案。

如果有人遇到类似的问题/解决方案,请发表评论。

答案 3 :(得分:0)

首先,在所有输入字段名称中添加@:

<input type="text" name="references[@].name"/>

其次,在提交之前添加调用函数:

<g:form action="save" onsubmit="replaceAllWildCardsWithConsecutiveNumbers();">

第三,这是您在提交表单之前调用的函数的代码:

function replaceAllWildCardsWithConsecutiveNumbers(){
    var inputs = $('form').find("[name*='@']");
    var names  = $.map(inputs, function(el) { return el.name });
    var uniqueNames = unique(names);

    for (index in uniqueNames) {
        var uniqueName = uniqueNames[index];                                    
        replaceWildCardsWithConsecutiveNumbers("input", uniqueName);                        
        replaceWildCardsWithConsecutiveNumbers("select", uniqueName);
    }
}

function unique(array){
    return array.filter(function(el, index, arr) {
        return index === arr.indexOf(el);
    });
}

function replaceWildCardsWithConsecutiveNumbers(inputName, name){
    counter = 0;
    $(inputName + "[name='" + name + "']").each(function (i, el) {
        var curName = $(this).attr('name');
        var newName = curName.replace("@", counter);
        $(this).attr('name', newName);
        counter += 1;
    });
}

基本上,replaceAllWildCardsWithConsecutiveNumbers()的代码是为名称包含@的所有输入(或选择)元素创建一个列表。删除重复项。然后迭代它们用数字替换@。

如果您有一个表,并且在第一次创建域类时将值提交到命令对象的列表,则此方法很有用。如果你正在更新,我猜你必须将计数器的值更改为更高的值。

我希望这可以帮助别人,因为我自己一直坚持这个问题。