Handling parameters from dynamic form for one-to-many relationships in grails

故事扮演 提交于 2019-12-02 00:59:32
Arthur Ronald

disclaimer: i do not know if the following approach works when using grails. Let me know later.

See better way for dynamic forms. The author says:

To add LineItems I have some js that calculates the new index and adds that to the DOM. When deleting a LineItem i have to renumber all the indexes and it is what i would like to avoid

So what i do

I have a variable which stores the next index

var nextIndex = 0;

When the page is loaded, i perform a JavaScript function which calculates how many child The collection has and configure nextIndex variable. You can use JQuery or YUI, feel free.

Adding a child statically

I create a variable which store the template (Notice {index})

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

When the user click on the Add child button, i replace {index} - by using regex - by the value stored in the nextIndex variable and increment by one. Then i add to the DOM

See also Add and Remove HTML elements dynamically with Javascript

Adding a child dinamically

Here you can see The Paolo Bergantino solution

By removing

But i think it is the issue grow up when deleting. No matter how many child you remove, does not touch on the nextIndex variable. See here

/**
  * 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"/>

Suppose i remove childList1 What i do ??? Should i renumber all the indexes ???

On the server side i use AutoPopulatingList. Because childList1 has been removed, AutoPopulatingList handles it as null. So on the initialization i do

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

This way, my collection just contains two child (without any null value) and i do not need to renumber all the indexes on the client side

About adding/removing you can see this link where i show a scenario wich can gives you some insight.

See also Grails UI plugin

Thanks,

Your answer brought some insight for me to do a wider search and I actually found a great post that covers all the inputs in my question. This is just a reference for anyone reading this. I will write a blog entry on how I implemented my case soon, but this link should provide a good source of ino with a working exmaple.

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

Most of the time I use ajax to manage such problem.

So when the user clicks add new phone I get the template UI from the server for manageability purpose ( the UI just same GSP template that I use to edit, update the phone), so this way you are not mixing your UI with your js code, whenever you want to change the UI you have to deal only with our GSP code.

Then after getting the UI I add it to the page using jquery DOM manipulation. Then after filling the form when they hit add(save) the request is sent to the server via ajax and is persisted immediately.

When the user clicks edit phone the same UI template is loaded from the server filled with existing phone data, then clicking update will update the corresponding phone immediately via ajax, and same thing applies to delete operation.

But one day I got an additional scenario for the use case that says, "until I say save contact no phone shall be saved on the backend, also after adding phones to the contact on the ui if navigate away to another page and come back later to the contact page the phones I added before must be still there." ugh..

To do this I started using the Session, so the above operations I explained will act on the phone list object I stored on the session instead of the DB. This is simple perform all the operation on the phonesInSession but finally dont forget to do this(delete update):

phonesToBeDeleted = phonesInDB - phonesInSession 

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

I know I dont have to put a lot of data in session but this is the only solution I got for my scenario.

If someone has got similar problem/solution please leave a comment.

First, in all your input fields names you add an @:

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

Second, add call a function before submitting:

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

Third, this is the code for the function that you call before submitting the form:

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

Basically, what the code for replaceAllWildCardsWithConsecutiveNumbers() does, is to create a list for all input (or select) elements whose name contains an @. Removes the duplicates. And then iterates over them replacing the @ with a number.

This works great if you have a table and you are submitting the values to a command object's list when creating a domain class for the first time. If you are updating I guess you'll have to change the value of counter to something higher.

I hope this helps someone else since I was stuck on this issue for a while myself.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!