0

Update: for those flagging this to be closed as a duplicate, the supposed duplicate question is nothing like what I am asking. My problem is I do not know until render time what the question set will be, how many questions there will be or what the question types will be so I cannot use the technique described in the "possible duplicate" answer.

Part of our JSF 2.x application has a requirement to render sets of questions to the user where the questions and the question types are not known until run-time. e.g we have something like (getters/setters omitted for clarity) :

public class QuestionSet {
    private List<Section> sections;
}

public class Section {
    private String sectionTitle;
    private List<Question> questions;
    private SectionStatus status; // e.g. UNANSWERED, CURRENTLY_ANSWERING,ANSWERED, COMPLETED
}

public class Question {
    private String questionText;
    private QuestionType questionType; // E.G TEXT, RADIO, LIST, CHECKBOX
    private List<String> options; // for RADIO/LIST/CHECKBOX types
    private List<String> answers;
}

We need to render each section in a seperate div, depending on it's status (e.g. UNANSWERED would display a div with just the title, ANSWERED would display a div with the section title and a green tick mark, and CURRENTLY_ANSWERING would render a div with the section title and then each question with the appropriate input control based on the question type.

The questions are also dynamic during the run - e.g. if a user answers yes to a radio button question, this may prompt further sub-questions.

I am currently doing this using a binding, i.e.

<h:panelGroup binding = "#{bean.panelGroup}" /> 

and within the bean's getPanelGroup creating the component tree by hand usin things like HtmlPanelGroup, HtmlOutputText, UIInput with ValueExpressions etc. which works fine but on reading some of BalusC's answers, particlarly to this question: How does the 'binding' attribute work in JSF? When and how should it be used? I am wondering if there is a "better" approach?

One of the things that concerns me is that the getter is called during RECREATE_VIEW for reasons explained in the linked question (after invoking the method referred to in the binding) so unless I take steps to, in RECREATE_VIEW phase, just return the component I created in the last RENDER_RESPONSE phase, this introduces unnecessary expense of recreating something I've just created.

In this case, it also seems pointless that JSF calls my setter to set the thing I just gave it in the getter for the bound property. (my bean is View scope as I will need to use ajax for some of the functionality our users require)

Thoughts/opinions (Especially from the ever helpful BalusC) greatly appreciated...

Community
  • 1
  • 1
Steve Atkinson
  • 1,219
  • 2
  • 12
  • 30
  • possible duplicate of [JSF: Dynamically change form](http://stackoverflow.com/questions/5654269/jsf-dynamically-change-form) – Johny T Koshy Oct 31 '13 at 02:58
  • For details on how binding works check: http://stackoverflow.com/questions/14911158/how-binding-attribute-in-jsf-works – Johny T Koshy Oct 31 '13 at 02:58
  • @johny, I don;t wish to sound snarky, but you haven't read my question properly. It is most definately not a duplicate of the first link in your comment - as I said, I do not know until I query the database how many questions there will be or what the question types will be so I cannot use ajax in the wy your "possible dupe" question does. Also, I linked to that same binding post in my text! In fact, that is what prompted my question. – Steve Atkinson Oct 31 '13 at 06:11
  • Related: http://stackoverflow.com/questions/3510614/how-to-create-dynamic-jsf-1-2-form-fields/ Targeted on JSF 1.2, but approach is almost same for JSF2. In case of binding you only need to replace lazy loading in getter by a `` listener method on the parent component. This doesn't change behavior but is just more "clean". – BalusC Oct 31 '13 at 10:50
  • @Balus I'd fprgpttpn about the jsf2 events (we've only recenty changed to jsf2) - That would clean up the code a little. I personally don't like the "try and render one of everything" approach in the related question you linked - our question panel is actually more complex that I stated - in that we may or may not have additional guidance text, inline help, and for certain text inputs, a search button - so to try and do it all in EL would get very ugly very quickly. I think as you have not come up with any strong objection to using bindings in the way I am doing, I will continue to do so. – Steve Atkinson Oct 31 '13 at 11:55

2 Answers2

1

I don't see much reason to use component binding in this case. You can decide in your view what to render and how. You can have <ui:fragment>/<c:if> to conditionally render elements basing on question type, <ui:repeat>/<c:forEach> to handle the question set, etc.

So, if I understand the workflow correctly, your question set will be determined in e.g. post constructor method:

@PostConstruct
public void init() {
    questionSet = service.get();//get it somehow
}

Then you'll have a set of sections and each of these section will contain questions, or answers, and validity is to be checked via AJAX. If I understand you right, then you can have the following view:

<h:form id="q-set">
    <ui:repeat value="#{bean.questionSet.sections}" var="section">
        <div>#{section.title}</div>
        <div class="#{section.status eq 'UNANSWERED' ? 'section-unanswered' : ... }"/>
        <ui:fragment rendered="#{section.status eq 'ANSWERED' ?}"><div class="tick"/></ui:fragment> ...
        <ui:fragment rendered="#{section.status eq 'ANSWERED' ?}">
            <ui:repeat value="#{section.questions}" var="question">
                <div>#{question.title}</div>
                <ui:fragment rendered="#{question.type eq 'RADIO'}">
                    <h:selectOneRadio value="#{question.answers[0]}" validator="...">
                        <f:selectItems value="#{question.options}" var="opt" itemLabel="#{opt}" ... />
                        <f:ajax ...>
                    </h:selectOneRadio>
                </ui:fragment>
                ...
            </ui:repeat>
        </ui:fragment>
    </ui:repeat>
</h:form>
skuntsel
  • 11,624
  • 11
  • 44
  • 67
  • No! yet again, I DO NOT KNOW IN ADVANCE WHAT THE QUESTION TYPES WILL BE! so in your case, you've shown a selectOneRadio, but it might be an h:inpuText, or an h:selectBooleanCheckbox or an h:selectOneMenu etc. and each question in the set may be of a different type. e.g I could get "do you like cats?" radio button question, then a "what colour is your cat?" needing a dropdown list, then a "please comment" which requires an input text box. Or I could get "What are your 3 favorite cookies?" and a checkbox list of cookie choices, then "How old are you?" drop down list etc. – Steve Atkinson Oct 31 '13 at 08:11
  • 1
    Do you see the ellipsis in the inner iterative component? You need to place another fragment there so that every question type is covered and, thus, every HTML DOM element is rendered, depending on question type. E.g. `` and later `` etc. – skuntsel Oct 31 '13 at 08:46
0

It looks like you are going to have too much logic/conditions in your view.

What about generating the view programmatically on the Java side ?

For tricky parts you may resort to JavaScript and JSON.

Christophe Roussy
  • 16,299
  • 4
  • 85
  • 85