9

(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.

Jimmy
  • 16,123
  • 39
  • 133
  • 213
  • 1
    Sounds like yes, you'd want to save the survey. Alternatively, you could make all this a Flow, and remove the "select parent" part of the edit page. Flows are good for multi-page creation processes. – billjamesdev Aug 30 '12 at 15:32
  • C'mon bro, do you read the answers? You should to use command objects and a collection decorated with LazyList and ideally javascript/jquery. What do you want my friend? – AA. Sep 03 '12 at 12:45
  • You should add to recipe a service to get transaction support – AA. Sep 03 '12 at 12:55

4 Answers4

4

Using that user interface you should create, save and add. A better approach is to create a master/detail user interface. You can see that approach here:

https://github.com/leadVisionary/Grails-Survey-Module

http://programmingitch.blogspot.com/2009/10/data-binding-for-one-to-many.html

http://omarello.com/2010/08/grails-one-to-many-dynamic-forms/

http://java.dzone.com/articles/creating-master-detail-forms

http://kapilpandit.wordpress.com/2009/02/25/complex_form_grails/

AA.
  • 4,496
  • 31
  • 35
4

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

Matthias
  • 1,759
  • 16
  • 34
Chris
  • 8,031
  • 10
  • 41
  • 67
  • this is looking like the most viable option so far, but it does depend on using static scaffolding (or at least implementing some form of view myself). The reason I ask is because I'd prefer to use dynamic scaffolding; less coding, less maintenance, and less effort ;) (in theory, theres always a catch) – Jimmy Sep 05 '12 at 21:00
2

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.

Rob Fletcher
  • 8,317
  • 4
  • 31
  • 40
  • To clarify. I'd personally use command objects as @crudolf suggests or just go all out & use a richer front-end such as Angular but this could work for you if you are determined to use scaffolding. – Rob Fletcher Sep 05 '12 at 15:03
0

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!

Roberto Perez Alcolea
  • 1,408
  • 11
  • 11