Reputation: 1471
I'm Using Grails 2.3.11. I have an issue in saving one-to-many dynamic forms.
I referred to this question Grails one-to-many databinding with dynamic forms
I'm posting as a new question since I could not add comment to the above referred post. So don't mark it as duplicate.
I created dynamic form based on this blog http://omarello.com/2010/08/grails-one-to-many-dynamic-forms/
The same worked for me in my previous application which using Grails 2.2.3
Domain Classes:
Contact Class
package blog.omarello
import org.apache.commons.collections.FactoryUtils
import org.apache.commons.collections.list.LazyList
class Contact {
static constraints = {
firstName(blank:false)
lastName(blank:false)
}
String firstName
String lastName
String nickName
List phones = new ArrayList()
static hasMany = [ phones:Phone ]
static mapping = {
phones cascade:"all-delete-orphan"
}
}
Phone Class
package blog.omarello
class Phone {
int index
String number
PhoneType type
boolean deleted
static transients = [ 'deleted' ]
static belongsTo = [ contact:Contact ]
/* Constraints & Enum */
}
Controller Action:
@Transactional
def save(Contact contactInstance) {
println 'params: -> '+params
println '------------------------------------------------------------'
println 'params.phones: -> '+params?.phones
println '------------------------------------------------------------'
println 'params.firstName: -> '+params?.firstName
println '------------------------------------------------------------'
println 'params.lastName: -> '+params?.lastName
println '------------------------------------------------------------'
println 'params.nickName: -> '+params?.nickName
if (!contactInstance.save(flush: true)) {
flash.error = message(code: 'default.not.created.message', args: [message(code: 'contact.label', default: 'Contact')])
render(view: "create", model: [contactInstance: contactInstance])
return
}
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.created.message', args: [message(code: 'contact.label', default: 'Contact'), contactInstance.id])
redirect contactInstance
}
'*' { respond contactInstance, [status: CREATED] }
}
}
When i submit the form, I didnt get any error in console. Input form fields name are phones[0].number, phones[ 1].number, ...
Console:
params: -> [lastName:Developer, phones[0].number:123456789, phones[0]:[id:, deleted:false, new:true, number:123456789, type:H], phones[1].number:987654321, phones[1]:[id:, deleted:false, new:true, number:987654321, type:H], phones[1].deleted:false, create:Create, phones[0].id:, phones[0].deleted:false, phones[1].id:, phones[1].type:H, nickName:admin, phones[0].type:H, phones[0].new:true, firstName:Grails, phones[1].new:true, action:save, format:null, controller:contact]
------------------------------------------------------------
params.phones: -> null
------------------------------------------------------------
params.firstName: -> Grails
------------------------------------------------------------
params.lastName: -> Developer
------------------------------------------------------------
params.nickName: -> admin
I'm not sure why params.phone shows null in Grails 2.3.11 Someone help me to fix this issue. Thanks in advance.
Upvotes: 1
Views: 1573
Reputation: 11
From what I've seen, the LazyList.decorate(...) cannot be used in Grails 2.3+ to bind dynamically created many-to-one objects. I have run into the exact same problem as you. I am upgrading a Grails 1.3 project to 2.7 and a specific portion of the project wasn't working. After much head to keyboard colissions, I realized an effective solution is to perform the binding within the controller. Due to the structure of the database underlying my project, I did not want to pass back a count of the dynamically created child objects, thus I use a loop with a sufficiently high index to capture all use cases. I could not for the life of me figure out how to interpolate a variable into the argument of the array in the param, e.g. param.'field[variable]'.
What does work, is to capture it as a list, as shown below. Then you can iterate without having to resort to a massive brute force piece of hideousness in your controller, for each array index.
Here is an example from my project . .
Domain
class CalculatorScenario {
List<"CalculatorScenarioIncome> calculatorScenarioIncome
}
Controller
def scenario = new CalculatorScenario()
scenario.properties = params
scenario.agent = getAgent()
//Iterate through the income sources
for (int i = 0; i < 30; i++) {
//Due to the vagaries of the GrailsParameterMap, we must retrieve the incomeSource as a list of lists
def incomeSourceList = params.list('expandableCalculatorScenarioIncome[' + i + ']')
//Break down the lists
def incomeSource
for (each in incomeSourceList) {
incomeSource = each
}
//Append the many Incomes to the Parent
if (incomeSource) {
CalculatorScenarioIncome income = new CalculatorScenarioIncome(scenario: scenario)
income.amount = Double.parseDouble(incomeSource.amount)
income.ownerType = incomeSource.ownerType
income.incomeSourceType = incomeSource.incomeSourceType
income.deleted = incomeSource.deleted
income.startAge = Integer.parseInt(incomeSource.startAge)
income.endAge = Integer.parseInt(incomeSource.endAge)
scenario.calculatorScenarioIncome.add(income)
}
}
log.info stop
It's messy, but it works.
Upvotes: 1
Reputation: 534
It's seem have a problem with data-binding. Try to remove id:
in params if it's a new phone. Tested with grails 2.3.7 and using it in my project.
Here is what I changed in _phone.gsp
<div id="phone${i}" class="phone-div" <g:if test="${hidden}">style="display:none;"</g:if>>
<g:if test="${phone?.id != null}">
<g:hiddenField name='phones[${i}].id' value='${phone?.id}'/>
</g:if>
...
</div>
Upvotes: 2