Summary
When a user is logged in he can create a new project. Every Project can have one Form. The Form exists out of multiple Fields. Every Field has a FieldType which says it the Field is an text, integer or date field.
We also need to have a FieldValue object to save the data entered from the form. The object model looks like this:
The java code for the entities are as follows:
Project.java
@Entity
public class Project implements Serializable {
private static final long serialVersionUID = -4007194693192812404L;
private long id;
private String title;
private String description;
private Form form;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
public Form getForm() {
return form;
}
public void setForm(Form form) {
this.form = form;
}
}
Form.java
@Entity
public class Form implements Serializable {
/**
*
*/
private static final long serialVersionUID = 4809850392387342995L;
private long id;
private String name;
private Project project;
private List<Field> fields = new ArrayList<Field>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
public List<Field> getFields() {
return fields;
}
public void setFields(List<Field> fields) {
this.fields = fields;
}
}
Field.java
@Entity
public class Field implements Serializable {
/**
*
*/
private static final long serialVersionUID = -4033197506447392008L;
private long id;
private long version;
private String name;
private FieldType type;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@OneToOne
public FieldType getType() {
return type;
}
public void setType(FieldType type) {
this.type = type;
}
}
FieldType.java
@Entity
public class FieldType {
private long id;
private long version;
private String type;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
FieldValue.java
@Entity
public class FieldValue implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1753348629073571409L;
private long id;
private long version;
private String text;
private Integer number;
private Date date;
private Field field;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Version
public long getVersion() {
return version;
}
public void setVersion(long version) {
this.version = version;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
@ManyToOne
public Field getField() {
return field;
}
public void setField(Field field) {
this.field = field;
}
}
Client.java
@Entity
public class Client implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1734916727086491089L;
private long id;
private long version;
private List<FieldValue> values = new ArrayList<FieldValue>();
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setVersion(long version) {
this.version = version;
}
@Version
public long getVersion() {
return version;
}
@OneToMany
public List<FieldValue> getValues() {
return values;
}
public void setValues(List<FieldValue> values) {
this.values = values;
}
}
ProjectAction.java
@Name("projectAction")
@Scope(ScopeType.CONVERSATION)
public class ProjectAction {
@In(create = true)
private ProjectHome projectHome;
@In(create = true)
private FormHome formHome;
@In(create = true)
private ClientHome clientHome;
@In(create = true)
private FormBinding formBinding2;
@In(create = true)
private FieldValueHome fieldValueHome;
private Project selectedProject;
private List<Project> projects;
@Logger
private static Log log;
public String persist(){
log.debug("try to persist project");
Project project = projectHome.getInstance();
Form form = formHome.getInstance();
project.setForm(form);
projectHome.persist();
return "persisted";
}
public String saveClient(){
log.debug("try to save client");
getFormValues();
clientHome.persist();
projectHome.update();
return "persisted";
}
public String update(){
log.debug("try to update project");
//Project project = projectHome.getSelectedProject();
projectHome.update();
return "updated";
}
public String remove(){
return "removed";
}
public String save() {
return "/form2.xhtml";
}
public void getFormValues() {
List<UIComponent> components = formBinding2.getHtmlPanelGrid().getChildren();
Client client = clientHome.getInstance();
Project project = projectHome.getInstance();
getComponentValuesRecursive(components, client, project);
}
public void getComponentValuesRecursive(List<UIComponent> components, Client client, Project project){
for(UIComponent component : components) {
log.debug("getId from component: " + component.getId());
log.debug("getFamily from component: " + component.getFamily());
if(component.getFamily().equals("javax.faces.Input")) {
HtmlInputText htmlInputText = (HtmlInputText)component;
log.debug("getValue from component: " + htmlInputText.getValue());
String fieldType = htmlInputText.getLabel();
String fieldName = htmlInputText.getId();
FieldValue value = new FieldValue();
if(fieldType.equals("string")) {
value.setText(((HtmlInputText)component).getValue().toString());
}
if(fieldType.equals("integer")) {
value.setNumber(Integer.parseInt(((HtmlInputText)component).getValue().toString()));
}
for(Field field : project.getForm().getFields()) {
if(field.getName().equals(fieldName)) {
value.setField(field);
}
}
client.getValues().add(value);
fieldValueHome.setInstance(value);
fieldValueHome.persist();
}
if(component.getFamily().equals("org.richfaces.Calendar")) {
//log.debug("getValue from component: " + ((HtmlCalendar)component).getValue());
//FieldValue value = new FieldValue();
//value.setText(((HtmlCalendar)component).getValue().toString());
}
if(component.getChildren().size() > 0) {
getComponentValuesRecursive(component.getChildren(), client, project);
}
}
}
public void loadForm() {
log.debug("entering loadForm");
HtmlPanelGrid htmlPanelGrid = new HtmlPanelGrid();
//projectHome.setId(new Long(1));
Project project = projectHome.getInstance();
Form form = project.getForm();
log.debug("form name: " + form.getName());
List<Field> fields = form.getFields();
log.debug("field size: " + fields.size());
int i = 0;
for(Field field : fields) {
i = i + 1;
log.debug("field type: " + field.getType().getType());
if(field.getType().getType().equals("string")){
log.debug("fieldtype is string");
HtmlInputText textInput = new HtmlInputText();
textInput.setId(field.getName());
textInput.setLabel(field.getType().getType());
textInput.setRequired(true);
htmlPanelGrid.getChildren().add(textInput);
}
if(field.getType().getType().equals("integer")){
log.debug("fieldtype is string");
HtmlInputText textInput = new HtmlInputText();
textInput.setId(field.getName());
textInput.setLabel(field.getType().getType());
textInput.setRequired(true);
htmlPanelGrid.getChildren().add(textInput);
}
if(field.getType().getType().equals("date")){
log.debug("fieldtype is date");
// HtmlCalender calendar = new HtmlCalender();
}
}
formBinding2.setHtmlPanelGrid(htmlPanelGrid);
}
public Project getSelectedProject() {
return selectedProject;
}
public void setSelectedProject(Project selectedProject) {
this.selectedProject = selectedProject;
}
public List<Project> getProjects() {
return projects;
}
public void setProjects(List<Project> projects) {
this.projects = projects;
}
public void retrieveProjects() {
projects = projectHome.retrieveProjects();
}
public void takeSelection() {
log.debug("take selection");
log.debug("selectedProject is: " + getSelectedProject().getTitle());
log.debug("selectedProject has got this amount of fields: " + getSelectedProject().getForm().getFields().size());
log.debug("first fieldname: " + getSelectedProject().getForm().getFields().get(0).getName());
projectHome.setId(selectedProject.getId());
loadForm();
}
}
In the loadForm() I dynamically add the HtmlInputText and set the Id to the name of the Field.getName() and I use the label to set the type of the field. I use these properties again in the getFormValues() method.
How can I create the
<s:decorate>
dynamicly in java code?
FormBinding.java
@Name("formBinding2")
@Scope(ScopeType.EVENT)
public class FormBinding {
private HtmlPanelGrid htmlPanelGrid;
public void setHtmlPanelGrid(HtmlPanelGrid htmlPanelGrid) {
this.htmlPanelGrid = htmlPanelGrid;
}
public HtmlPanelGrid getHtmlPanelGrid() {
return htmlPanelGrid;
}
}
The formbinding.java needs to be in EVENT scope, also see 7.11. Conversational components and JSF component bindings in Seam documentation.
project.xhtml
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
template="layout/template.xhtml">
<ui:define name="body">
<h1>Welcome to Seam!</h1>
<rich:panel>
<h:panelGrid columns="2">
<h:form>
<rich:panel>
<s:validateAll>
<s:decorate id="projectNameField" template="/layout/edit.xhtml">
<ui:define name="label">Projectnaam</ui:define>
<h:inputText id="projectName" value="#{project.title}" />
</s:decorate>
</s:validateAll>
<div class="actionButtons" >
<h:commandButton value="save" action="#{projectAction.save}"/>
<s:button id="discardProject" value="Discard changes" propagation="end" view="/home.xhtml" rendered="#{projectHome.managed}"/>
<s:button id="cancelProject" value="Cancel" propagation="end" view="/#{empty projectFrom ? 'projectList' :projectFrom}.xhtml" rendered="#{!projectHome.managed}"/>
</div>
</rich:panel>
</h:form>
</h:panelGrid>
</rich:panel>
</ui:define>
</ui:composition>
project.page.xml (page actions)
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"
no-conversation-view-id="/home.xhtml"
login-required="false">
<begin-conversation join="true" flush-mode="manual"/>
</page>
form2.xhtml
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
template="layout/template.xhtml">
<ui:define name="body">
<h1>Welcome to Seam!</h1>
<rich:panel>
<h:form id="formForm">
<h:panelGrid columns="2" >
<rich:panel>
<s:decorate id="formNameField" template="/layout/edit.xhtml">
<ui:define name="label">form name</ui:define>
<h:inputText id="formName" value="#{form.name}" />
</s:decorate>
<a4j:form ajaxSingle="true">
<s:decorate id="customFormNameField" template="/layout/edit.xhtml">
<ui:define name="label">custom form field</ui:define>
<h:inputText id="formName" value="#{field.name}" />
<h:selectOneMenu id="listboxTypes" value="#{field.type}">
<s:selectItems value="#{formHome.types}" var="_fieldType" label="#{_fieldType.type}" itemValue="#{_child}" noSelectionLabel="please select" />
<s:convertEntity/>
</h:selectOneMenu>
<a4j:commandButton action="#{formHome.addField}" value="add" reRender="fields"/>
</s:decorate>
</a4j:form>
</rich:panel>
<rich:panel>
<rich:dataList id="fields" value="#{form.fields}" var="_field">
<h:outputText value="#{_field.name}"/>
</rich:dataList>
</rich:panel>
<div class="actionButtons" >
<h:commandButton id="saveForm" value="Save" action="#{projectAction.persist}" rendered="#{!projectHome.managed}"/>
<h:commandButton id="updateForm" value="Update" action="#{projectAction.update}" rendered="#{projectHome.managed}"/>
<h:commandButton id="deleteForm" value="Delete" action="#{projectAction.remove}" rendered="#{projectHome.managed}"/>
<s:button id="discardForm" value="Discard changes" propagation="end" view="/home.xhtml" rendered="#{projectHome.managed}"/>
<s:button id="cancelForm" value="Cancel" propagation="end" view="/#{empty formFrom ? 'formList' :formFrom}.xhtml" rendered="#{!projectHome.managed}"/>
</div>
</h:panelGrid>
</h:form>
</rich:panel>
</ui:define>
</ui:composition>
form2.page.xml
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"
no-conversation-view-id="/home.xhtml"
login-required="false">
<begin-conversation join="true" flush-mode="manual" />
<action execute="#{formHome.retrieveFieldTypes}"/>
<navigation from-action="#{formHome.addField}">
<end-conversation/>
<redirect view-id="/form2.xhtml"/>
</navigation>
<navigation from-action="#{projectAction.persist}">
<end-conversation/>
<redirect view-id="/formbinding2.xhtml"/>
</navigation>
</page>
formbinding2.xhtml
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
template="layout/template.xhtml">
<ui:define name="body">
<h1>Welcome to Seam!</h1>
<rich:panel>
<h:form>
<h:panelGrid columns="2">
<s:decorate id="projectField" template="/layout/edit.xhtml">
<ui:define name="label">Project:</ui:define>
<h:selectOneMenu value="#{projectAction.selectedProject}">
<s:selectItems value="#{projectAction.projects}" var="_project" label="#{_project.title}"
noSelectionLabel="please select"/>
<s:convertEntity />
<a4j:support event="onchange" reRender="panelGrid" action="#{projectAction.takeSelection}" ajaxSingle="true"/>
</h:selectOneMenu>
</s:decorate>
</h:panelGrid>
</h:form>
</rich:panel>
</ui:define>
</ui:composition>
form2binding.page.xml
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"
no-conversation-view-id="/home.xhtml"
login-required="false">
<begin-conversation join="true" flush-mode="manual" />
<action execute="#{projectAction.retrieveProjects}"/>
<navigation from-action="#{projectAction.takeSelection}">
<redirect view-id="/form3.xhtml"></redirect>
</navigation>
</page>
form3.xhtml
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
template="layout/template.xhtml">
<ui:define name="body">
<h1>Welcome to Seam!</h1>
<rich:panel>
<h:form>
<h:panelGrid id="panelGrid" columns="2" binding="#{formBinding2.htmlPanelGrid}" />
<div class="actionButtons" >
<h:commandButton id="saveProject" value="Save" action="#{projectAction.saveClient}" />
</div>
</h:form>
</rich:panel>
</ui:define>
</ui:composition>
I needed to create this extra page with the page action because a reRender of the panelGrid didn't worked. Otherwise I had added the the code in the formbinding2.xhtml.
form3.page.xml
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://jboss.com/products/seam/pages"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"
no-conversation-view-id="/home.xhtml"
login-required="false">
<begin-conversation join="true" flush-mode="manual" />
<action execute="#{projectAction.loadForm}"/>
</page>
When I have the time I will complete this howto but any help is much appreciated!