mcgyver5
mcgyver5

Reputation: 1163

Ajax call in Grails leads to "Could not find matching constructor"

I use the grails formRemote tag to add an item to a dropdown list "on the fly". The item does get added, but when I submit the main form I get:

Could not find matching constructor for: VarnType(java.lang.String)

where Varn is the domain object being created in the main form and VarnType is the domain object listed in the dropdown,the list I am adding to with formRemote. Here is the formRemote usage:

<g:form url="[resource:varnInstance, action:'save']" id="mainV">
            <fieldset class="form">
                <g:render template="form"/>
            </fieldset>
            <fieldset class="buttons">
                <g:submitButton name="create" class="save" value="${message(code: 'default.button.create.label', default: 'Create')}" />
            </fieldset>
        </g:form>
        <g:formRemote name="vTypeForm"
                      url="[controller:'varnType', action:'createVarnTypeOnTheFly']"
                      update="varnType">
            <input type="text" name="typeName" >
            <input type="submit" value="Create Varn Type!">
        </g:formRemote>

Thinking that the VarnType object needed another constructor with an argument of String, I added it, but got, in the controller of the Varn domain object itself:

Class
org.hibernate.TransientObjectException
Message
object references an unsaved transient instance - save the 
transient instance before flushing

I don't understand why the Varn domain object (the parent object) is trying to persist a VarnType (child object) that was already created via the formRemote. Is there an additional step I should take to make this object available to Grails?

EDIT: Here is controller code:

def createVarnTypeOnTheFly(){

    VarnType vt = new VarnType(params)
    vt.save flush: true
    render(template: '/config/varnTypeOptions',model:[optionList: VarnType.list()])
}

Here is the entity relationship:

class VarnType {
String typeName

static constraints = {
}

static mapping = {
    id column:"varn_type_id"
    id generator: "sequence",params: [sequence:"scan_sequence"]
}

String toString(){
    return typeName
}

}

VarnType:

class Varn {
    Application app
    Assessment assessment
    String varnName
    String recommendation
    String httpMethod
    VarnType varnType
    String varnNote
    String param
    Risk risk
    String urlParam
    Date dateFound
    Date dateReported
    Date dateFixed
    static hasMany = [varnNotes:AppNote]

    static constraints = {
        varnName nullable: true
        urlParam nullable: true
        risk nullable: true
        recommendation nullable: true
        assessment nullable: true

    }
    static mapping = {
        id column:'varn_id'
        id generator: 'sequence',params:[sequence:'scan_sequence']
    }

    }

Thanks.

Upvotes: 1

Views: 635

Answers (1)

Jeff Scott Brown
Jeff Scott Brown

Reputation: 27245

I expect that you have a request parameter named varnType and you are doing something like new Varn(params). When the data binder tries to initialize the varnType property in the Varn instance, since you haven't told it otherwise it us trying to call new VarnType(params.varnType), which fails.

If you want the binder to be able to create an instance of VarnType from a String you will need to do something like provide a custom Converter or use the BindUsing annotation. All of that is covered in the user guide. See http://grails.org/doc/latest/guide/theWebLayer.html#dataBinding.

Upvotes: 3

Related Questions