JSF PrimeFaces adds several empty entries that are only required when the main form is submitted


Assume we have a form. One p:inputText visible but user can add many more using p:commandButton. All this values have to be provided when submitting with another p:commandButton. Issue arises when user tries to add more than one empty input fields. All of them are marked required="true" so validation error appears when one field is empty and user try to add another.

The best would be to allow to add as many fields as user needs, then fill them in and submit.


<h:form id="myForm">
    <p:commandButton value="add" actionListener="#{testBean.addNewItem()}" update="@form"/>
    <p:commandButton value="done" update="@form,:p"/>
    <ui:repeat value="#{testBean.list}" var="l">
        <p:inputText value="#{l.name}" required="true"/>
<p:messages autoUpdate="true"/>
<p:panel id="p">

Backing bean does nothing fancy. Only provides getter and setter for list. It also adds empty string to the list.

public class TestBean implements Serializable {
    private List<Item> list = new ArrayList<Item>();

    public List<Item> getList() { return list; }

    public void setList(List<Item> list) { this.list = list; }

    public void addNewItem() { list.add(new Item()); }

I could:

  1. Remove requirement for field - not an option.
  2. Add immediate="true" for adding button. Validation is not a problem now but it causes all values that was filled in but not submitted to disappear. And I need to update @form because only then newly added fields will be rendered by ui:repeat.
  3. I tried to add process="@this" for adding button. Unfortunately that didn't change a thing. Input field values are not processed, but form needs to be updated. I am loosing not submitted values as above.

What am I missing? Is there any workaround?

Just let the required attribute check if the "done" button is pressed. The button's own client ID is present as a request parameter if that's the case. Request parameters are available by #{param} mapping. You can use button's binding attribute to bind the physical component to the view so that you can grab its UIComponent#getClientId() elsewhere. Finally just do the boolean logic.


<p:commandButton binding="#{done}" ... />
<p:inputText ... required="#{not empty param[done.clientId]}" />