Using the web mvc add-on Spring Roo generates views with crud functionality for each entity. For a given entity, Roo will typically generate views for create, show, list and update that map to methods on the generated Spring controller. By specifying attributes to the RooWebScaffold annotation, it is possible to control which views get generated.
However, there are some situations where it may be desirable to allow updating multiple entities (parent-child) from within the same view, perhaps to provide a better user experience. For example, consider a domain model with Business and User entities having a one-to-one relationship to an Address entity (managed from the Business/User). When creating a new Business or User entity, it would provide a better user experience to allow entering all the Business or User details and Address details from a single view, rather than having to create the Address from a separate view and then select the address identifier from a drop-down list within the create Business/User view (as would be the default behaviour with Roo generation).
To achieve this behaviour, the create.jspx view can be augmented with additional fields to enter the address details. Note the use of the dot-notation to navigate to the nested property of the address object.
<form:create id="fc_com_changesoft_samples_domain_Business" modelAttribute="business" path="/businesses" render="${empty dependencies}" z="0qN2h7loptVj50JEP4Htm9WcQ2U=">
<field:select field="businessType" id="c_com_changesoft_samples_domain_Business_businessType" items="${businesstypes}" path="businesstypes" z="lbHfbOekwe5M/bFdLTQsGY43NTs="/>
<field:input field="businessName" id="c_com_changesoft_samples_domain_Business_businessName" z="H/Qzpi7gDgY45Ag3tLqLZiYL9wY="/>
<field:input field="registrationNumber" id="c_com_changesoft_samples_domain_Business_registrationNumber" z="v0EWCe/zpM89BvaLz1O6+1p1ZEs="/>
<field:input field="businessDescription" id="c_com_changesoft_samples_domain_Business_description" z="oly643Jm218C3sMXDHNnxbCaKC8="/>
<field:input field="address.addressLine" id="c_com_changesoft_samples_domain_Address_addressLine" z="nDT/wm1FdsgfzEXLv9zJkmZybmg="/>
<field:select field="address.addressType" id="c_com_changesoft_samples_domain_Address_addressType" items="${addresstypes}" path="addresstypes" z="aYI9UiQIlRB+F0Bwu93/eo4ZFAc="/>
<field:input field="address.city" id="c_com_changesoft_samples_domain_Address_city" z="FVwmWn28G7JtqIfUIvAdZORMVkM="/>
<field:input field="address.postcode" id="c_com_changesoft_samples_domain_Address_postcode" z="eezB9GBw9FiIb+ISiJosM7zXbi4="/>
</form:create>
This results in the following rendered jsp view.
When the form is submitted, it correctly persists a new Business with a new Address entity (due to the address attribute of Business having cascading behaviour).
The update.jspx view requires slightly more work. When the update form of an entity is submitted, the id and version attributes are also present as hidden form fields and sent as request parameters to identify and retrieve the detached entity and update it. Therefore when updating 2 entities from a single view, it is also necessary to encapsulate the id and version attributes of the Address entity as hidden form fields; and subsequently submit them along with the Business attributes. The jsp tags that are provided by the web mvc add-on do not cater for hidden form fields. Therefore to fulfil this requirement it was necessary to create a custom tag that would generate an html hidden form field that could then be used within the update view jsp.
The update.jspx view requires slightly more work. When the update form of an entity is submitted, the id and version attributes are also present as hidden form fields and sent as request parameters to identify and retrieve the detached entity and update it. Therefore when updating 2 entities from a single view, it is also necessary to encapsulate the id and version attributes of the Address entity as hidden form fields; and subsequently submit them along with the Business attributes. The jsp tags that are provided by the web mvc add-on do not cater for hidden form fields. Therefore to fulfil this requirement it was necessary to create a custom tag that would generate an html hidden form field that could then be used within the update view jsp.
<c:set value="hidden" var="type" />
<c:choose>
<c:when test="${disableFormBinding}">
<input id="_${sec_field}_id" name="${sec_field}" type="${fn:escapeXml(type)}" />
</c:when>
<c:otherwise>
<form:hidden id="_${sec_field}_id" path="${sec_field}" />
<br />
<form:errors cssClass="errors" id="_${sec_field}_error_id" path="${sec_field}" />
</c:otherwise>
</c:choose>
With the new hidden.tagx custom tag file, the update view for the Business entity is adjusted to mark any required fields as hidden:
<field:hidden field="address.id" id="c_com_changesoft_samples_domain_Address_Id" z="user-managed"/>
<field:hidden field="address.version" id="c_com_changesoft_samples_domain_Address_Version" z="user-managed"/>
Using the new hidden field tags, the update view renders as follows:
This view allows updating both the Business and Address entities by correctly sending the id and version properties for each entity as hidden form fields, shown below in the HTML source.
<div style="display: none;" id="_c_com_changesoft_samples_domain_Address_Id_id">
<input id="_address.id_id" name="address.id" type="hidden" value="1"/>
<br />
</div>
<div style="display: none;" id="_c_com_changesoft_samples_domain_Address_Version_id">
<input id="_address.version_id" name="address.version" type="hidden" value="2"/>
<br />
</div>
<div id="_c_com_changesoft_samples_domain_Address_addressLine_id">
<label for="_address.addressLine_id">
Address Line
:
</label>
<input id="_address.addressLine_id" name="address.addressLine" type="text" value="119 Middlesex Street"/>
This works quite well without requiring any custom code within the controller.