Hi.
We are using JSF 1.2 and Seam 2.2.
I have found some similar questions like this one, however there are so many ways this can be done that it made me more confused.
We are getting a XML
file that we are reading. This XML
contains information on some form fields that needs to be presented.
So I created this custom DynamicField.java
that has all the information we need.
@Data //lombok annotation generating boilerplate code
public class DynamicField {
String label; //label of the field
String fieldKey; // some key to identify the field
String fieldValue; //the value of field
String type; //can be input,radio,selectbox etc
}
So we have a List<DynamicField>
.
I want to iterate through this list and populate the form fields so it looks something like this:
<h:dataTable value="#{dynamicList} var="dyn">
<kf:ourCustomComponent value="#{dyn}"/>
</h:dataTable>
The <kf:ourCustomComponent>
would then return the appropriate JSF form components ie (label, inputText etc)
OR
Another approach would be to just display the <kf:ourCustomComponent>
and then that would return a HtmlDataTable
with form elements. (I think this is maybe easier to do).
My questions
- Which approach is best?
- Can someone show me to some links or code where it shows how I can create this? I prefer complete code examples, and not answers like "`You need a subclass of javax.faces.component.UIComponent`".
UPDATE
This is how I solved this. Any feedback/comments appreciated
I updated my DynamicField.java to be
@Data //lombok annotation generating boilerplate code
public class DynamicField {
String label; //label of the field
String fieldKey; // some key to identify the field
String fieldValue; //the value of field
String list; //comma separated list that I later transform to List<String> to be used with SelectMany, CheckMany etc
Type type = Type.TEXT; //default
transient Boolean option = Boolean.FALSE; //This is a transient variable used for Radio, CheckOne etc
public enum Type {
TEXT, SECRET, TEXTAREA, RADIO, SELECTONE, SELECTMANY, CHECKONE, CHECKMANY;
@Override
public String toString() {
return this.name();
}
}
/**
* We need to do this check because if it is a radio, and the value is 'true'
* @return
*/
@XmlTransient
public Boolean getOption() {
option = Boolean.valueOf(this.fieldValue);
return option;
}
public void setOption(Boolean option) {
this.option = option;
this.fieldValue = String.valueOf(option);
}
}
I put all the values in a Map<String, ArrayList<DynamicField>>
and I create a List which is a new JavaBean containing the title (key in the map) and ArrayList<DynamicField>
@ToString
public class SubjectDynamicField implements Serializable {
@Getter private final String title;
//Must use ArrayList because List will not work in JAXB
@Getter private final ArrayList<DynamicField> fields;
public SubjectDynamicField(String title, ArrayList<DynamicField> fields) {
if(fields == null) {
throw new NullPointerException("DynamicFields cannot be null");
}
this.title = title;
this.fields = fields;
}
}
I populate this List like this:
List<SubjectDynamicField> subjectDynamicField = new ArrayList<SubjectDynamicField>();
for (String key : getDynamicFields().keySet()) {
SubjectDynamicField sd = new SubjectDynamicField(key, getDynamicFields().get(key));
subjectDynamicField.add(sd);
}
And in the XHTML I iterate over this list (Can you iterate over a map in Facelets?)
<ui:repeat value="#{subjectDynamicField}" var="sdf" rendered="#{not empty subjectDynamicField}" id="dynamicField">
<h2 class="title"><a href="#">#{sdf.title}</a></h2>
<div class="tightPanel">
<fieldset class="fieldSet" style="border:0;margin-bottom:0.7em;">
<ui:repeat value="#{sdf.fields}" var="df">
<s:label>#{df.fieldKey}</s:label>
<h:inputText value="#{df.fieldValue}" rendered="#{df.type == 'TEXT'}" />
<h:inputSecret value="#{df.fieldValue}" rendered="#{df.type == 'SECRET'}" />
<h:inputTextarea value="#{df.fieldValue}" rendered="#{df.type == 'TEXTAREA'}" />
<!-- Select one -->
<h:selectOneRadio title="#{df.fieldKey}" label="#{df.fieldKey}" value="#{df.option}" rendered="#{df.type == 'RADIO'}">
<f:selectItem itemLabel="#{messages['yes']}" itemValue="#{true}" />
<f:selectItem itemLabel="#{messages['no']}" itemValue="#{false}" />
</h:selectOneRadio>
<h:selectOneMenu title="#{df.fieldKey}" label="#{df.fieldKey}" value="#{df.option}" rendered="#{df.type == 'SELECTONE'}">
<f:selectItem itemLabel="#{messages['yes']}" itemValue="#{true}" />
<f:selectItem itemLabel="#{messages['no']}" itemValue="#{false}" />
</h:selectOneMenu>
<h:selectBooleanCheckbox title="#{df.fieldKey}" label="#{df.fieldKey}" value="#{df.option}" rendered="#{df.type == 'CHECKONE'}">
<f:selectItem itemLabel="#{messages['yes']}" itemValue="#{true}" />
<f:selectItem itemLabel="#{messages['no']}" itemValue="#{false}" />
</h:selectBooleanCheckbox>
<!-- Select many -->
<h:selectManyMenu value="#{df.choicesSelected}" rendered="#{df.type == 'SELECTMANY'}">
<s:selectItems value="#{df.choices}" var="c" label="#{c}"/>
</h:selectManyMenu>
<h:selectManyCheckbox value="#{df.choicesSelected}" rendered="#{df.type == 'CHECKMANY'}">
<s:selectItems value="#{df.choices}" var="c" label="#{c}"/>
</h:selectManyCheckbox>
<br/>
</ui:repeat>
</fieldset>
</div>
</ui:repeat>