I'm working on old system which has a lot of code written back in the days using jsf1.2. Now I have the task to dynamically add/remove group of input elements via ajax on button click.
I was guided by this post on how to add input fields dynamically, made little tweaks to support ajax and made prove of concept which works perfectly fine.
Now the problem occurs when instead of <h:inputText> .. </h:inputText>
or other form fields I use our own custom tag component.
When using the custom tag I get the page displayed on page load, but if I try to remove an item, for the first component in the list I get the error java.io.IOException: Cannot add the same component twice: editform:parentTable:0:fieldsTable:0:HTML_TITLE_INPUT_en
I believe the problems is due to the component already being in the component tree when the custom tag tries to add it again on postback, but still I don't know how to fix the issue.
Note 1: I have to use our custom tag since it has a lot of logic inside and it is used to display every type of form field (inputText, textarea, checkboxes etc)
xhtml code:
<h:dataTable id="parentTable" value="#{bean.groups}" var="group" styleClass="parentTableStyleClass">
<h:column>
<h:dataTable id="fieldsTable" value="#{group.fields}" var="field">
<h:column>
<h:outputLabel value="#{field.label}" styleClass="col-sm-2 control-label" />
</h:column>
<h:column>
<customTag:propertyValue
name="#{field.name}"
operation="edit"
instance="#{bean.instance}"
styleClass="form-control"
inputWrapperClass="col-sm-5"/>
</h:column>
</h:dataTable>
</h:column>
<h:column>
<p:commandButton value="Remove Group"
styleClass="btn btn-secondary btn-sm"
action="#{bean.removeGroup(group)}"
process="@(.parentTableStyleClass)"
update="@(.parentTableStyleClass)" />
</h:column>
</h:dataTable>
The tag class:
public void encodeBegin(FacesContext context) throws IOException {
getChildren().clear();
...
// bunch of logic
...
HtmlPanelGroup wrapper = new HtmlPanelGroup();
HtmlInputText inputText = new HtmlInputText();
...
// bunch of code creating input text field
...
wrapper.setStyleClass(getInputWrapperClass() + " input-group");
wrapper.getChildren().add(inputText);
getChildren().add(wrapper);
}