DynaForm gives access to the client ids of its controls and lets you visit all controls with a visit callback, for inter-control communication, clearing inputs and removing rows at runtime.
The attribute varContainerId names a variable holding the client id prefix within pe:dynaFormControl, which gives the whole client id of a component inside it. That is sometimes required for JavaScript or PrimeFaces.current().ajax().update(...).
Every input here has an icon to select a value from a dialog; the value is set into the input after a click on "OK".
ExecutableVisitCallback is the PrimeFaces Extensions implementation of the JSF VisitCallback. It runs any VisitTaskExecutor on all controls within pe:dynaForm, which allows inter-control communication.
Here ClearInputsExecutor clears all inputs / selects of one row (see clearInputs in ClearDynaFormController). Rows are removed at runtime with removeRegularRow(...) / removeExtendedRow(...) of the DynaFormModel.
Click "Clear inputs" and "Remove" to see it in action.
<style>
.dynaform-container .pe-dynaform-cell input,
.dynaform-container .pe-dynaform-cell[colspan="1"] input {
width: 150px;
}
.dynaform-visit .pe-dynaform-cell {
vertical-align: top;
}
.dynaform-visit .clearLink {
vertical-align: middle;
}
.dynaform-visit .pe-dynaform-cell .p-spinner input {
width: 60px;
}
</style>
<div class="card dynaform-container">
<h5>Utilization of container client Id</h5>
<p>The attribute <code>varContainerId</code> names a variable holding the client id prefix within <code>pe:dynaFormControl</code>, which gives the whole client id of a component inside it. That is sometimes required for JavaScript or <code>PrimeFaces.current().ajax().update(...)</code>.
Every input here has an icon to select a value from a dialog; the value is set into the input after a click on "OK".</p>
<h:form id="formContainerClientId">
<h:panelGroup id="dynaFormGroup">
<p:messages id="messages" showSummary="true"/>
<pe:dynaForm id="dynaForm" value="#{containerDynaFormController.model}"
var="data" varContainerId="ccId">
<pe:dynaFormControl for="txt">
<h:panelGroup style="white-space: nowrap;">
<p:inputText id="txt" value="#{data.value}"/>
<p:commandButton icon="pi pi-search" process="@this txt"
update=":formContainerClientId:dialogList" oncomplete="PF('dialogListWdgt').show()"
title="Please choose an item"
style="border:none;">
<f:setPropertyActionListener value="#{ccId}"
target="#{containerDynaFormController.containerClientId}"/>
<f:setPropertyActionListener value="#{data}"
target="#{containerDynaFormController.selectedField}"/>
<f:setPropertyActionListener value="#{data.value}"
target="#{containerDynaFormController.selectedItem}"/>
</p:commandButton>
</h:panelGroup>
</pe:dynaFormControl>
<f:facet name="buttonBar">
<p:commandButton value="Submit" process="dynaForm"
update=":formContainerClientId:dynaFormGroup :formContainerClientId:inputValues"
oncomplete="PF('inputValuesWidgetContainer').show()"/>
</f:facet>
</pe:dynaForm>
</h:panelGroup>
<p:dialog id="dialogList" widgetVar="dialogListWdgt">
<f:facet name="header">
Please select an item
</f:facet>
<h:selectOneListbox id="selectList" value="#{containerDynaFormController.selectedItem}"
style="width:200px;">
<f:selectItems value="#{containerDynaFormController.list}"/>
</h:selectOneListbox>
<h:panelGroup layout="block" style="white-space:nowrap; margin-top:15px;">
<p:commandButton value="OK" icon="pi pi-save" style="margin-right:5px"
process="@this selectList" action="#{containerDynaFormController.updateSelection}"
global="false" oncomplete="PF('dialogListWdgt').hide()"/>
<p:commandButton value="Cancel" type="button" onclick="PF('dialogListWdgt').hide()"/>
</h:panelGroup>
</p:dialog>
<p:dialog header="Input values" widgetVar="inputValuesWidgetContainer">
<p:dataList id="inputValues" value="#{containerDynaFormController.values}" var="value"
style="margin:10px;">
<h:outputText value="#{value}" style="margin-right: 15px;"/>
</p:dataList>
</p:dialog>
</h:form>
</div>
<div class="card dynaform-visit">
<h5>Access controls with visit callback and remove rows</h5>
<p><code>ExecutableVisitCallback</code> is the PrimeFaces Extensions implementation of the JSF <code>VisitCallback</code>. It runs any <code>VisitTaskExecutor</code> on all controls within <code>pe:dynaForm</code>, which allows inter-control communication.
Here <code>ClearInputsExecutor</code> clears all inputs / selects of one row (see <code>clearInputs</code> in <code>ClearDynaFormController</code>). Rows are removed at runtime with <code>removeRegularRow(...)</code> / <code>removeExtendedRow(...)</code> of the <code>DynaFormModel</code>.
Click "Clear inputs" and "Remove" to see it in action.</p>
<h:form id="formVisitCallback">
<pe:dynaForm id="dynaForm" value="#{clearDynaFormController.model}" var="condition">
<pe:dynaFormControl type="column">
<p:selectOneMenu id="tableColumn" value="#{condition.tableColumn}">
<f:selectItem itemValue="" itemLabel=""/>
<f:selectItem itemValue="model" itemLabel="model"/>
<f:selectItem itemValue="manufacturer" itemLabel="manufacturer"/>
<f:selectItem itemValue="year" itemLabel="year"/>
<f:attribute name="rcIndex" value="#{condition.index}"/>
</p:selectOneMenu>
</pe:dynaFormControl>
<pe:dynaFormControl type="offset">
<p:spinner id="inputOffset" value="#{condition.inputOffset}" min="0" max="99">
<p:keyFilter mask="num"/>
<f:attribute name="rcIndex" value="#{condition.index}"/>
</p:spinner>
</pe:dynaFormControl>
<pe:dynaFormControl type="operator">
<p:selectOneMenu id="valueOperator" value="#{condition.valueOperator}">
<f:selectItem itemValue="eq" itemLabel="equal"/>
<f:selectItem itemValue="not" itemLabel="not equal"/>
<f:selectItem itemValue="gt" itemLabel="greater"/>
<f:selectItem itemValue="lt" itemLabel="lesser"/>
<f:attribute name="rcIndex" value="#{condition.index}"/>
</p:selectOneMenu>
</pe:dynaFormControl>
<pe:dynaFormControl type="value">
<p:inputText id="inputValue" value="#{condition.inputValue}">
<f:attribute name="rcIndex" value="#{condition.index}"/>
</p:inputText>
</pe:dynaFormControl>
<pe:dynaFormControl type="clear" styleClass="clearLink">
<p:commandLink value="Clear inputs" process="@this"
action="#{clearDynaFormController.clearInputs(condition.index)}"
global="false"/>
<p:commandLink value="Remove" process="@this" update="dynaForm"
action="#{clearDynaFormController.removeCondition(condition)}"
global="false" style="margin-left:10px"/>
</pe:dynaFormControl>
</pe:dynaForm>
<p:commandButton value="Submit form and show current values" process="dynaForm" update="conditions"
style="margin:15px 5px 0 0" oncomplete="PF('condValuesDlg').show()"/>
<p:commandButton value="Show current values without submit" process="@none" update="conditions"
style="margin-top:15px;" oncomplete="PF('condValuesDlg').show()"/>
<p:dialog header="Condition Values" widgetVar="condValuesDlg">
<h:panelGroup id="conditions" layout="block" style="margin:10px;">
<h:outputText value="#{clearDynaFormController.conditions}" escape="false"/>
</h:panelGroup>
</p:dialog>
</h:form>
</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.component.UINamingContainer;
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.FormField;
/**
* ContainerDynaFormController
*
* @author Oleg Varaksin / last modified by $Author$
* @version $Revision$
*/
@Named
@ViewScoped
public class ContainerDynaFormController implements Serializable {
@Serial
private static final long serialVersionUID = 20130426L;
private DynaFormModel model;
private List<SelectItem> list;
private FormField selectedField;
private String selectedItem;
private String containerClientId; // container clientId of the currently handled input field
@PostConstruct
protected void initialize() {
list = new ArrayList<SelectItem>();
list.add(new SelectItem("Item 1", "Item 1"));
list.add(new SelectItem("Item 2", "Item 2"));
list.add(new SelectItem("Item 3", "Item 3"));
list.add(new SelectItem("Item 4", "Item 4"));
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("Input 1");
final DynaFormControl control12 = row.addControl(new FormField("Some text"));
label11.setForControl(control12);
final DynaFormLabel label13 = row.addLabel("Input 2");
final DynaFormControl control14 = row.addControl(new FormField("Some text"));
label13.setForControl(control14);
// 2. row
row = model.createRegularRow();
final DynaFormLabel label21 = row.addLabel("Input 3");
final DynaFormControl control22 = row.addControl(new FormField("Some text"));
label21.setForControl(control22);
final DynaFormLabel label23 = row.addLabel("Input 4");
final DynaFormControl control24 = row.addControl(new FormField("Some text"));
label23.setForControl(control24);
}
public void updateSelection() {
final FacesContext fc = FacesContext.getCurrentInstance();
// update selected item value for the currently handled field
selectedField.setValue(selectedItem);
// update the corresponding input field in UI
PrimeFaces.current().ajax().update(containerClientId + UINamingContainer.getSeparatorChar(fc) + "txt");
}
public List<String> getValues() {
if (model == null) {
return null;
}
final List<String> values = new ArrayList<String>();
for (final DynaFormLabel dynaFormLabel : model.getLabels()) {
values.add(
dynaFormLabel.getValue() + ": " + ((FormField) dynaFormLabel.getForControl().getData()).getValue());
}
return values;
}
public DynaFormModel getModel() {
return model;
}
public List<SelectItem> getList() {
return list;
}
public FormField getSelectedField() {
return selectedField;
}
public void setSelectedField(final FormField selectedField) {
this.selectedField = selectedField;
}
public String getSelectedItem() {
return selectedItem;
}
public void setSelectedItem(final String selectedItem) {
this.selectedItem = selectedItem;
}
public String getContainerClientId() {
return containerClientId;
}
public void setContainerClientId(final String containerClientId) {
this.containerClientId = containerClientId;
}
}
package org.primefaces.extensions.showcase.model.dynaform;
import java.io.Serial;
import java.io.Serializable;
import java.util.List;
import jakarta.faces.model.SelectItem;
/**
* FormField
*
* @author Oleg Varaksin / last modified by $Author$
* @version $Revision$
*/
public class FormField implements Serializable {
@Serial
private static final long serialVersionUID = 20120521L;
private Object value;
private boolean required;
private List<SelectItem> selectItems;
public FormField(Object value) {
this.value = value;
}
public FormField(boolean required) {
this.required = required;
}
public FormField(Object value, boolean required) {
this.value = value;
this.required = required;
}
public FormField(Object value, boolean required, List<SelectItem> selectItems) {
this.value = value;
this.required = required;
this.selectItems = selectItems;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public boolean isRequired() {
return required;
}
public void setRequired(boolean required) {
this.required = required;
}
public List<SelectItem> getSelectItems() {
return selectItems;
}
}
package org.primefaces.extensions.showcase.controller.dynaform;
import java.io.*;
import java.util.*;
import jakarta.annotation.*;
import jakarta.faces.component.visit.*;
import jakarta.faces.context.*;
import jakarta.faces.view.*;
import jakarta.inject.*;
import org.primefaces.extensions.component.dynaform.*;
import org.primefaces.extensions.model.dynaform.*;
import org.primefaces.extensions.showcase.model.dynaform.*;
import org.primefaces.extensions.util.visitcallback.*;
/**
* ClearDynaFormController
*
* @author Oleg Varaksin / last modified by $Author$
* @version $Revision$
*/
@Named
@ViewScoped
public class ClearDynaFormController implements Serializable {
protected static final Set<VisitHint> VISIT_HINTS = EnumSet.of(VisitHint.SKIP_UNRENDERED);
@Serial
private static final long serialVersionUID = 20130504L;
private DynaFormModel model;
private List<Condition> conditions;
@PostConstruct
protected void initialize() {
model = new DynaFormModel();
conditions = new ArrayList<>();
// 1. condition and row
Condition condition = new Condition("model", 2, "eq", "mercedes", 0);
conditions.add(condition);
DynaFormRow row = model.createRegularRow();
row.addControl(condition, "column");
row.addControl(condition, "offset");
row.addControl(condition, "operator");
row.addControl(condition, "value");
row.addControl(condition, "clear");
// 2. condition and row
condition = new Condition("manufacturer", 1, "not", "chrysler group", 1);
conditions.add(condition);
row = model.createRegularRow();
row.addControl(condition, "column");
row.addControl(condition, "offset");
row.addControl(condition, "operator");
row.addControl(condition, "value");
row.addControl(condition, "clear");
// 3. condition and row
condition = new Condition("year", 0, "lt", "2010", 2);
conditions.add(condition);
row = model.createRegularRow();
row.addControl(condition, "column");
row.addControl(condition, "offset");
row.addControl(condition, "operator");
row.addControl(condition, "value");
row.addControl(condition, "clear");
}
public static void clearInputs(final int index) {
final FacesContext fc = FacesContext.getCurrentInstance();
final DynaForm dynaForm = (DynaForm) fc.getViewRoot().findComponent(":formVisitCallback:dynaForm");
// Ids of components to be visited
final String[] ids = new String[] {"tableColumn", "inputValue", "inputOffset", "valueOperator"};
final VisitTaskExecutor visitTaskExecutor = new ClearInputsExecutor(fc.getELContext(), ids, index);
// clear inputs in the visit callback
final ExecutableVisitCallback visitCallback = new ExecutableVisitCallback(visitTaskExecutor);
dynaForm.visitTree(VisitContext.createVisitContext(fc, null, VISIT_HINTS), visitCallback);
}
public void removeCondition(final Condition condition) {
model.removeRegularRow(condition.getIndex());
conditions.remove(condition);
// re-index conditions
int idx = 0;
for (final Condition cond : conditions) {
cond.setIndex(idx);
idx++;
}
}
public String getConditions() {
final StringBuilder sb = new StringBuilder();
for (final Condition condition : conditions) {
sb.append(condition.toString());
sb.append("<br/>");
}
return sb.toString();
}
public DynaFormModel getModel() {
return model;
}
}
package org.primefaces.extensions.showcase.model.dynaform;
import java.io.Serial;
import java.io.Serializable;
/**
* Condition
*
* @author Oleg Varaksin / last modified by $Author: $
* @version $Revision: 1.0 $
*/
public class Condition implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String tableColumn;
private int inputOffset;
private String valueOperator;
private String inputValue;
private int index; // internal index in the list of all conditions
public Condition() {
}
public Condition(String tableColumn, int inputOffset, String valueOperator, String inputValue, int index) {
this.tableColumn = tableColumn;
this.inputOffset = inputOffset;
this.valueOperator = valueOperator;
this.inputValue = inputValue;
this.index = index;
}
public String getTableColumn() {
return tableColumn;
}
public void setTableColumn(String tableColumn) {
this.tableColumn = tableColumn;
}
public int getInputOffset() {
return inputOffset;
}
public void setInputOffset(int inputOffset) {
this.inputOffset = inputOffset;
}
public String getValueOperator() {
return valueOperator;
}
public void setValueOperator(String valueOperator) {
this.valueOperator = valueOperator;
}
public String getInputValue() {
return inputValue;
}
public void setInputValue(String inputValue) {
this.inputValue = inputValue;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Condition condition = (Condition) o;
if (index != condition.index) {
return false;
}
if (inputOffset != condition.inputOffset) {
return false;
}
if (inputValue != null ? !inputValue.equals(condition.inputValue) : condition.inputValue != null) {
return false;
}
if (tableColumn != null ? !tableColumn.equals(condition.tableColumn) : condition.tableColumn != null) {
return false;
}
if (valueOperator != null ? !valueOperator.equals(condition.valueOperator) : condition.valueOperator != null) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = tableColumn != null ? tableColumn.hashCode() : 0;
result = 31 * result + inputOffset;
result = 31 * result + (valueOperator != null ? valueOperator.hashCode() : 0);
result = 31 * result + (inputValue != null ? inputValue.hashCode() : 0);
result = 31 * result + index;
return result;
}
@Override
public String toString() {
return "Condition {"
+ "tableColumn='" + tableColumn + '\''
+ ", inputOffset=" + inputOffset
+ ", valueOperator='" + valueOperator + '\''
+ ", inputValue='" + inputValue + '\''
+ '}';
}
}