Reputation: 1590
I have simply hierarchy.
class Atom { String value}
class Unit {
List atoms
static hasMany = [ atoms:Atom ]
String name
}
class Block {
List unitss
static hasMany = [ units:Unit ]
String name
}
Let's say I want to create one block which contains two units with two atoms each. So, first, I create four atoms in atom form. Then, I create two units, and select previously created atoms. In the end, I create block and select previously created units. It isn't convenient, so I want to move all creation logic in single form. I used approach from this article http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/
In short, I added method in class Block:
def getExpandableUnitList() {
return LazyList.decorate(units,FactoryUtils.instantiateFactory(Unit.class))
}
Also, I added button which call js function:
var unitCount = ${blockInstance?.units.size()} + 0;
function addUnit()
{
var htmlId = "unit" + unitCount;
var deleteIcon = "${resource(dir:'images/skin', file:'database_delete.png')}";
var templateHtml = "<div id='" + htmlId + "' name='" + htmlId + "'>\n";
templateHtml += "Unit "+ (unitCount + 1) + "\n";
templateHtml += "<input type='hidden' id='expandableUnitList[" + unitCount + "].name' name='expandableUnitList[" + unitCount + "].name' />\n";
templateHtml += "<span onClick='$(\"#" + htmlId + "\").remove();'><img src='" + deleteIcon + "' /></span>\n";
templateHtml += "</div>\n";
$("#UnitList").append(templateHtml);
unitCount++;
}
Now, I can create block with units in one form(see the image below, sorry for bad form design)
Form 1 http://imageshack.us/a/img833/8775/screenshotfrom201210241.png
However, this form isn't convenient too - we can't add atoms to units.
So, I added lazylist to unit class, and added another button with almost the same js code. Now, I've got this:
Form 2 http://imageshack.us/a/img31/8775/screenshotfrom201210241.png
However, when I creating this form - it doesn't save my atoms. It is expected behaviour, because our unit's lazy list doesn't know about atom's lazy list. And I don't know how to manage that. The only thing I've tried - the below code, but it doesn't work.
templateHtml += "<input type='hidden' id='expandableBlockList[" + blockId + "].add(expandableUnitList[" + unitCount + "].name)' name='expandableBlockList[0].add(expandableUnitList[" + unitCount + "].name)' />\n";
So. my question is - how to create multiple nested objects in single form?
Upvotes: 1
Views: 4398
Reputation: 626
Take a look at the Grails documentation - specifically the section on Gorm cascading behavior, it explains what you are looking for.
... Whether it is a one-to-one, one-to-many or many-to-many, defining belongsTo will result in updates cascading from the owning class to its dependant (the other side of the relationship), and for many-/one-to-one and one-to-many relationships deletes will also cascade.
If you do not define belongsTo then no cascades will happen and you will have to manually save each object (except in the case of the one-to-many, in which case saves will cascade automatically if a new instance is in a hasMany collection).
Edit:
BTW - not sure if this is a typo, but your Block definition does not contain any Units
I was quick to submit an answer and probably should have given it more thought - it sounds like your issue is more on the form side vs the domain definition. Currently as your domain is defined, you should be able to create an Atom and it's relationship with Unit when you create a Unit. Here's a really simple form to illustrate what you need to do in terms of how to define the inputs.
<form ...
Unit: <input type="text" name="name" value="" />
Atom 1: <input type="text" name="atoms[0].value" value="" />
Atom 2: <input type="text" name="atoms[1].value" value="" />
...
</form>
The size of the atoms collection is hard-coded here for an example, but in a more scalable solution you would define them dynamically. So on submission, this would create the Unit, and it's 2 Atoms.
Edit: Have you tried something like this:
Block: <g:textField name="name" value=""/>
<br />
Unit 1: <g:textField name="units[0].name" value=""/> atoms:
<g:textField name="units[0].atoms[0].value" value=""/>
<g:textField name="units[0].atoms[1].value" value=""/>
<br />
Unit 2: <g:textField name="units[0].name" value=""/> atoms:
<g:textField name="units[0].atoms[0].value" value=""/>
<g:textField name="units[0].atoms[1].value" value=""/>
Upvotes: 1