DynaForm builds a dynamic form with labels, inputs, selects and any other elements from a model, for forms that are described at runtime (for example in a database or an XML file). It adds an expandable extended view area, open / close state saving, an autoSubmit feature, a client-side widget API and various facets.
Build a simple dynamic form: create a DynaFormModel, add rows with createRegularRow(), add labels with addLabel(...) and controls with addControl(data, type, ...), and link them with label.setForControl(control).
The child tag pe:dynaFormControl matches the Java controls by its type attribute (usually one to many). Labels are rendered automatically, no p:outputLabel is needed.
A nested model is like a table contained in a cell, which is useful when a control is a combination of several pe:dynaFormControl. The XHTML stays the same, only the Java model changes: create a second DynaFormModel, fill it as usual and add it with nestedRow.addModel(data, type, ...).
Normally a form can be built straightforwardly with h:panelGrid or p:panelGrid if the count of rows / columns and the positions of elements are known.
That is not possible if a form is described dynamically at runtime. DynaForm builds such a form from a Java model without limitations.
Supported facets: headerRegular, footerRegular, headerExtended, footerExtended and buttonBar.
<style>
.dynaform-basic .pe-dynaform-cell input,
.dynaform-basic .pe-dynaform-cell textarea,
.dynaform-basic .pe-dynaform-cell[colspan="1"] input,
.dynaform-basic .pe-dynaform-cell[colspan="1"] textarea {
width: 150px;
}
.dynaform-basic .pe-dynaform-cell.calendar input,
.dynaform-basic .pe-dynaform-cell[colspan="1"].calendar input {
width: 120px;
}
.dynaform-basic .pe-dynaform-cell .p-selectonemenu {
width: 134px;
}
.dynaform-basic .label-container {
font-weight: bold;
}
.dynaform-basic .field-container input {
text-transform: uppercase;
}
.dynaform-nested .pe-dynaform-cell .p-datepicker input {
width: 100px;
}
.dynaform-nested .pe-dynaform-headerfacet div {
border: 1px dotted #808080;
padding: 10px;
text-align: center;
}
.dynaform-nested .pe-dynaform-label {
text-align: left;
padding: 2px 20px 0 0;
}
</style>
<div class="card dynaform-basic">
<h5>Basic usage</h5>
<p>Build a simple dynamic form: create a <code>DynaFormModel</code>, add rows with <code>createRegularRow()</code>, add labels with <code>addLabel(...)</code> and controls with <code>addControl(data, type, ...)</code>, and link them with <code>label.setForControl(control)</code>.
The child tag <code>pe:dynaFormControl</code> matches the Java controls by its <code>type</code> attribute (usually one to many). Labels are rendered automatically, no <code>p:outputLabel</code> is needed.</p>
<h:form id="formBasicUsage">
<h:panelGroup id="dynaFormGroup">
<p:messages id="messages" showSummary="true"/>
<pe:dynaForm id="dynaForm" value="#{dynaFormController.model}" var="data" columnClasses="label-container, field-container">
<pe:dynaFormControl type="input" for="txt">
<p:inputText id="txt" value="#{data.value}" required="#{data.required}"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="calendar" for="cal" styleClass="calendar">
<p:datePicker id="cal" value="#{data.value}" required="#{data.required}" showIcon="true"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="select" for="sel">
<p:selectOneMenu id="sel" value="#{data.value}" required="#{data.required}">
<f:selectItems value="#{dynaFormController.languages}"/>
</p:selectOneMenu>
</pe:dynaFormControl>
<pe:dynaFormControl type="textarea" for="tarea">
<p:inputTextarea id="tarea" value="#{data.value}" required="#{data.required}" autoResize="false"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="rating" for="rat">
<p:rating id="rat" value="#{data.value}" required="#{data.required}"/>
</pe:dynaFormControl>
<f:facet name="buttonBar">
<p:commandButton value="Submit" action="#{dynaFormController.submitForm}"
process="dynaForm" update=":formBasicUsage:dynaFormGroup :formBasicUsage:inputValues"
oncomplete="handleCompleteBasicUsage(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
</f:facet>
</pe:dynaForm>
</h:panelGroup>
<p:dialog header="Input values" widgetVar="inputValuesBasicUsage">
<p:dataList id="inputValues" value="#{dynaFormController.bookProperties}" var="bookProperty"
style="margin:10px;">
<h:outputText value="#{bookProperty.name}: #{bookProperty.formattedValue}"
style="margin-right: 10px;"/>
</p:dataList>
</p:dialog>
</h:form>
<script>
/* <![CDATA[ */
function handleCompleteBasicUsage(xhr, status, args) {
if (args && args.isValid) {
PF('inputValuesBasicUsage').show();
} else {
PF('inputValuesBasicUsage').hide();
}
}
/* ]]> */
</script>
</div>
<div class="card dynaform-nested">
<h5>Utilization of nested model</h5>
<p>A nested model is like a table contained in a cell, which is useful when a control is a combination of several <code>pe:dynaFormControl</code>. The XHTML stays the same, only the Java model changes: create a second <code>DynaFormModel</code>, fill it as usual and add it with <code>nestedRow.addModel(data, type, ...)</code>.</p>
<h:form id="formNestedModel">
<h:panelGroup id="dynaFormGroup">
<p:messages id="messages" showSummary="true"/>
<pe:dynaForm id="dynaForm" value="#{nestedDynaFormController.model}" var="data">
<pe:dynaFormControl type="input" for="txt">
<p:inputText id="txt" value="#{data.value}" required="#{data.required}"/>
</pe:dynaFormControl>
<pe:dynaFormControl type="date" for="cal" styleClass="calendar">
<p:datePicker id="cal" value="#{data.value}" required="#{data.required}" showIcon="true"/>
</pe:dynaFormControl>
<f:facet name="headerRegular">
<div><h:outputText value="Search area"/></div>
</f:facet>
<f:facet name="buttonBar">
<p:commandButton value="Submit" action="#{nestedDynaFormController.submitForm}"
process="dynaForm" update=":formNestedModel:dynaFormGroup :formNestedModel:inputValues"
oncomplete="handleCompleteNestedModel(xhr, status, args)"/>
<p:commandButton type="reset" value="Reset" style="margin-left: 5px;"/>
</f:facet>
</pe:dynaForm>
</h:panelGroup>
<p:dialog header="Input values" widgetVar="inputValuesNestedModel">
<p:dataList id="inputValues" value="#{nestedDynaFormController.bookProperties}" var="bookProperty"
style="margin:10px;">
<h:outputText value="#{bookProperty.name}: #{bookProperty.formattedValue}"
style="margin-right: 10px;"/>
</p:dataList>
</p:dialog>
</h:form>
<script>
/* <![CDATA[ */
function handleCompleteNestedModel(xhr, status, args) {
if (args && args.isValid) {
PF('inputValuesNestedModel').show();
} else {
PF('inputValuesNestedModel').hide();
}
}
/* ]]> */
</script>
</div>
package org.primefaces.extensions.showcase.controller.dynaform;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.model.SelectItem;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import org.primefaces.PrimeFaces;
import org.primefaces.extensions.model.dynaform.DynaFormControl;
import org.primefaces.extensions.model.dynaform.DynaFormLabel;
import org.primefaces.extensions.model.dynaform.DynaFormModel;
import org.primefaces.extensions.model.dynaform.DynaFormRow;
import org.primefaces.extensions.showcase.model.dynaform.BookProperty;
/**
* DynaFormController
*
* @author Oleg Varaksin / last modified by $Author$
* @version $Revision$
*/
@Named
@ViewScoped
public class DynaFormController implements Serializable {
@Serial
private static final long serialVersionUID = 20120423L;
private static List<SelectItem> languages = new ArrayList<>();
private DynaFormModel model;
@PostConstruct
protected void initialize() {
model = new DynaFormModel();
// add rows, labels and editable controls
// set relationship between label and editable controls to support outputLabel
// with "for" attribute
// 1. row
DynaFormRow row = model.createRegularRow();
final DynaFormLabel label11 = row.addLabel("Author");
final DynaFormControl control12 = row.addControl(new BookProperty("Author", true), "input");
label11.setForControl(control12);
final DynaFormLabel label13 = row.addLabel("ISBN");
final DynaFormControl control14 = row.addControl(new BookProperty("ISBN", true), "input");
label13.setForControl(control14);
// 2. row
row = model.createRegularRow();
final DynaFormLabel label21 = row.addLabel("Title");
final DynaFormControl control22 = row.addControl(new BookProperty("Title", false), "input", 3, 1);
label21.setForControl(control22);
// 3. row
row = model.createRegularRow();
final DynaFormLabel label31 = row.addLabel("Publisher");
final DynaFormControl control32 = row.addControl(new BookProperty("Publisher", false), "input");
label31.setForControl(control32);
final DynaFormLabel label33 = row.addLabel("Published on");
final DynaFormControl control34 = row.addControl(new BookProperty("Published on", false), "calendar");
label33.setForControl(control34);
// 4. row
row = model.createRegularRow();
final DynaFormLabel label41 = row.addLabel("Language");
final DynaFormControl control42 = row.addControl(new BookProperty("Language", false), "select");
label41.setForControl(control42);
final DynaFormLabel label43 = row.addLabel("Description", 1, 2);
final DynaFormControl control44 = row.addControl(new BookProperty("Description", false), "textarea", 1, 2);
label43.setForControl(control44);
// 5. row
row = model.createRegularRow();
final DynaFormLabel label51 = row.addLabel("Rating");
final DynaFormControl control52 = row.addControl(new BookProperty("Rating", 3, true), "rating");
label51.setForControl(control52);
}
public DynaFormModel getModel() {
return model;
}
public List<BookProperty> getBookProperties() {
if (model == null) {
return null;
}
final List<BookProperty> bookProperties = new ArrayList<>();
for (final DynaFormControl dynaFormControl : model.getControls()) {
bookProperties.add((BookProperty) dynaFormControl.getData());
}
return bookProperties;
}
public String submitForm() {
final FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
final boolean hasErrors = sev != null && FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0;
PrimeFaces.current().ajax().addCallbackParam("isValid", !hasErrors);
return null;
}
public List<SelectItem> getLanguages() {
if (languages.isEmpty()) {
languages.add(new SelectItem("en", "English"));
languages.add(new SelectItem("de", "German"));
languages.add(new SelectItem("ru", "Russian"));
languages.add(new SelectItem("tr", "Turkish"));
}
return languages;
}
}
package org.primefaces.extensions.showcase.controller.dynaform;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.annotation.PostConstruct;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
import org.primefaces.PrimeFaces;
import org.primefaces.extensions.model.dynaform.DynaFormControl;
import org.primefaces.extensions.model.dynaform.DynaFormLabel;
import org.primefaces.extensions.model.dynaform.DynaFormModel;
import org.primefaces.extensions.model.dynaform.DynaFormRow;
import org.primefaces.extensions.showcase.model.dynaform.BookProperty;
/**
* AnotherDynaFormController
*
* @author Sébastien Lepage / last modified by $Author$
* @version $Revision$
*/
@Named
@ViewScoped
public class NestedDynaFormController implements Serializable {
@Serial
private static final long serialVersionUID = 20120423L;
private DynaFormModel model;
@PostConstruct
protected void initialize() {
model = new DynaFormModel();
// add rows, labels and editable controls
// set relationship between label and editable controls to support outputLabel
// with "for" attribute
// 1. regular row
DynaFormRow row = model.createRegularRow();
// 1.1 Nested model
DynaFormModel nestedModel = new DynaFormModel();
DynaFormRow nestedRow1 = nestedModel.createRegularRow();
final DynaFormLabel label11 = nestedRow1.addLabel("Firstname");
final DynaFormLabel label12 = nestedRow1.addLabel("Lastname");
nestedRow1 = nestedModel.createRegularRow();
final DynaFormControl control21 = nestedRow1.addControl(new BookProperty("firstname", true), "input");
final DynaFormControl control22 = nestedRow1.addControl(new BookProperty("lastname", true), "input");
label11.setForControl(control21);
label12.setForControl(control22);
row.addModel(nestedModel, 2, 1);
// 2. regular row
row = model.createRegularRow();
final DynaFormLabel label31 = row.addLabel("Date from");
// 2.1 Nested model
nestedModel = new DynaFormModel();
final DynaFormRow nestedRow2 = nestedModel.createRegularRow();
final DynaFormControl control32 = nestedRow2.addControl(new BookProperty("beginDate", false), "date");
label31.setForControl(control32);
final DynaFormLabel label33 = nestedRow2.addLabel("till");
final DynaFormControl control34 = nestedRow2.addControl(new BookProperty("endDate", false), "date");
label33.setForControl(control34);
row.addModel(nestedModel);
// 2.2 Nested model
nestedModel = new DynaFormModel();
final DynaFormRow nestedRow3 = nestedModel.createRegularRow();
final DynaFormLabel label36 = nestedRow3.addLabel("Located from");
final DynaFormControl control37 = nestedRow3.addControl(new BookProperty("locationStart", "Lane A", false),
"input");
label36.setForControl(control37);
final DynaFormLabel label38 = nestedRow3.addLabel("To");
final DynaFormControl control39 = nestedRow3.addControl(new BookProperty("locationEnd", "Lane B", false),
"input");
label38.setForControl(control39);
row.addModel(nestedModel);
}
public DynaFormModel getModel() {
return model;
}
public String submitForm() {
final FacesMessage.Severity sev = FacesContext.getCurrentInstance().getMaximumSeverity();
final boolean hasErrors = sev != null && FacesMessage.SEVERITY_ERROR.compareTo(sev) >= 0;
PrimeFaces.current().ajax().addCallbackParam("isValid", !hasErrors);
return null;
}
public List<BookProperty> getBookProperties() {
if (model == null) {
return null;
}
final List<BookProperty> bookProperties = new ArrayList<BookProperty>();
for (final DynaFormControl dynaFormControl : model.getControls()) {
bookProperties.add((BookProperty) dynaFormControl.getData());
}
return bookProperties;
}
}