2

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

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

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

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

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

4 回答 4

1

免责声明:我不知道以下方法在使用 grails 时是否有效。稍后告诉我。

查看动态表单的更好方法。作者说:

要添加 LineItems,我有一些计算新索引并将其添加到 DOM 的 js。删除 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 变量中的值并加一。然后我添加到 DOM

另请参阅使用 Javascript 动态添加和删除 HTML 元素

动态添加一个孩子

在这里您可以看到 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();
   }
});

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

关于添加/删除,你可以看到这个链接,我展示了一个场景,可以给你一些洞察力。

另请参阅Grails UI 插件

于 2010-08-19T06:28:36.623 回答
1

谢谢,

您的回答为我进行更广泛的搜索带来了一些见解,实际上我发现了一篇很棒的帖子,涵盖了我问题中的所有输入。这仅供阅读本文的任何人参考。我将很快写一篇关于我如何实现我的案例的博客文章,但是这个链接应该提供一个很好的 ino 来源和一个工作示例。

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

于 2010-08-20T12:17:31.687 回答
0

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

因此,当用户单击时,add new phone出于可管理性目的,我从服务器获取模板 UI(UI 与我用来编辑、更新手机的 GSP 模板相同),因此无论何时您都不会将您的 UI 与您的 js 代码混合在一起想要改变 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()
}

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

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

于 2015-04-09T13:46:39.877 回答
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() 的代码所做的是为名称包含@ 的所有输入(或选择)元素创建一个列表。删除重复项。然后遍历它们,用数字替换 @。

如果您有一个表并且在第一次创建域类时将值提交到命令对象的列表,这将非常有用。如果您正在更新,我想您必须将 counter 的值更改为更高的值。

我希望这对其他人有所帮助,因为我自己在这个问题上停留了一段时间。

于 2017-05-26T13:39:40.843 回答