Adding child elements onto a domain object before the domain object is created

4.2k Views Asked by At

(Sorry if this is a noob question, I couldn't find the answers on the grails reference)

I have the following domain heirarchy :

User > (has many) Survey > (has many) SurveyQuestion > (has many) SurveyQuestionResponse

These are two of the above :

class Survey {

    String surveyName

    static hasMany = [questions: SurveyQuestion]
    static belongsTo = [user:User]
    static constraints = {
    }
}

class SurveyQuestion {

    String question

    static hasMany = [responses : SurveyQuestionResponse]
    static belongsTo = [survey:Survey]

    static constraints = {
    }
}

When I create a Survey, I first see a screen like this :

(survey)

I fill in the survey name, then click add a survey question, and see the next screen :

(survey question)

But it requires a survey being set, which hasn't yet completed.

Question : Do I have to create and save the survey first, then edit it and add survey questions (each of which individually need to be created and saved before I can create responses), or is there a way to add child objects as I'm creating the parent objects?

I want to use dynamic scaffolding so I don't have to create controllers and views manually.

The questions and answers are entirely independent, and will not be re-used across the hierarchy.

4

There are 4 best solutions below

1
On BEST ANSWER

You should use command objects. This way you can comfortably add child elements while creating the parent. E.g.

class CreateSurveyCommand {
     String surveyName
     List<SurveyQuestion> surveyQuestions = 
         org.apache.commons.collections.list.LazyList.decorate(
             new ArrayList(), 
             new org.apache.commons.collections.functors.InstantiateFactory(SurveyQuestion.class))
}

In the view (assuming index.gsp) you have something like:

<g:textField name="surveyName" value="${cmd?.question}" />
<g:each in="${cmd.surveyQuestions}" var="surveyQuestion" status="i">
    <g:textField
        name="surveyQuestions[i].question" 
        value="${cmd?.surveyQuestions[i].question}" />
</g:each>
<g:actionSubmit action="addQuestion"/>

Having an addQuestion action within your controller:

def addAction(CreateSurveyCommand cmd) {
    cmd.surveyQuestions.add(new SurveyQuestion())
    render(view:"index", model: [cmd: cmd])
}

Editing is another topic, but works the same way.

Have a look at this blog post:

http://blog.andresteingress.com/2012/06/29/groovy-2-0-love-for-grails-command-objects

1
On

If you declare questions as a List in the Survey class then your views will be able to access them by index.

List questions
static hasMany = [questions: SurveyQuestion]

In your form GSP you can use something like this:

<g:textField
    name="questions[0].question"
    value="${surveyInstance?.questions?.size() > 0 ? surveyInstance?.questions[0]?.question : ''}"/>

The ternary for the value is a little crude but could easily be tucked away in your own tag.

If you really need to use dynamic scaffolding you can override individual views or the templates used to generate the view. Alternatively you could use the fields plugin to render the inputs for the questions property.

I've created a simple app & at that point everything appears to just work. I was expecting to have to write a PropertyEditor implementation to grow the list but it seems that Grails 2.1 will do this for you.

Reordering or removing questions would need more experimentation but updating the questions works, i.e. the question text is changed rather than a new SurveyQuestion instance being created.

0
On
0
On

If you are going to reuse the questions... maybe you shouldn't use a belongsTo =[survey:Survey]

Then if you don't have this relationship, you can create a template for creating the questions and add them to the question collection in survey object before saving it!