I am currently trying to do a simple list of inputs with adding/removing primefaces p:commandButton
.
I am using PrimeFaces 6.2 on Glassfish 4.1.1 with Mojarra 2.2.12.
ExampleBean.java
package /* myPackage */;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javafx.util.Pair;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
@Named(value = "exampleBean")
@SessionScoped
public class ExampleBean implements Serializable {
private List<Pair<Integer, Integer>> list;
@PostConstruct
public void init() {
list = new ArrayList<>();
addNewItem();
}
public void addNewItem() {
list.add(new Pair<>(1, 300));
}
public List<Pair<Integer, Integer>> getList() {
return list;
}
public void setList(List<Pair<Integer, Integer>> list) {
this.list = list;
}
}
example.xhtml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:jsf="http://xmlns.jcp.org/jsf">
<h:head>
<title>Example</title>
<meta charset="utf-8" />
</h:head>
<h:body>
<h:form id="example-form">
<div jsf:id="example-container">
<ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
<div>
<p:inputText value="#{item.key}" />
<p:inputText value="#{item.value}" />
<p:commandButton
value="Delete"
actionListener="#{exampleBean.list.remove(item)}"
process="@this"
update="example-form:example-container"
rendered="#{!status.first}" />
<p:commandButton
value="Add"
actionListener="#{exampleBean.addNewItem()}"
process="@this"
update="example-form:example-container"
rendered="#{status.first}" />
</div>
</ui:repeat>
</div>
</h:form>
</h:body>
</html>
With the rendered
attribute of each of my p:commandButton
, I want to display the add button only on the first item, and the delete button on all items except the first one (making it undeletable).
My problem here, is that using rendered="#{status.first}"
on the adding button make the whole thing not working.
<p:commandButton ... rendered="#{status.last}" /> <!-- actionListener called -->
<p:commandButton ... rendered="#{true}" /> <!-- actionListener called -->
<p:commandButton ... rendered="#{status.first}" /> <!-- actionListener NOT called -->
With rendered="#{status.first}"
, a button click do not call actionListener
but trigger the update
.
I do not have any idea what could change between displaying it in the first item rather than in others or last one.
I think this is releated to the <ui:repeat>
flag and the generation of unique ID's for the command buttons. The view is probably gets confused. The generated ID's change when you add additional buttons. Plus, introducing the rendered attribute also changes things in the way the component is processed.
So to solve this, I think you need to reprocess the ui:repeat
component and the command buttons in order to get valid action URL's. The solution is simple - remove process="@this"
.
I tweaked your example with a working solution (the example uses Lombok);
@Data @Named @ViewScoped
public class ExampleBean implements Serializable {
private List<IntegerPair> list;
@PostConstruct
private void init() {
list = new ArrayList<>();
addNewItem();
}
public void addNewItem() {
list.add(new IntegerPair(1, 300));
}
@Data
@AllArgsConstructor
public class IntegerPair {
private int key, value;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html"
xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui"
xmlns:jsf="http://xmlns.jcp.org/jsf">
<h:head>
<title>Example</title>
<meta charset="utf-8" />
</h:head>
<h:body>
Test!
<h:form id="example-form">
<div jsf:id="example-container">
<ui:repeat value="#{exampleBean.list}" var="item" varStatus="status">
<div>
<p:inputText value="#{item.key}" />
<p:inputText value="#{item.value}" />
<p:commandButton
value="Delete"
actionListener="#{exampleBean.list.remove(item)}"
update="example-form:example-container"
rendered="#{!status.first}" />
<p:commandButton
value="Add"
actionListener="#{exampleBean.addNewItem}"
update="example-form:example-container"
rendered="#{status.first}" />
</div>
</ui:repeat>
</div>
</h:form>
</h:body>
</html>
Notice the IntegerPair
class? I had to introduce this because the Pair<>
class defined in JavaFX does not have a writable key property - writeable properties are required by JSF when it processes the backing bean value of input components.
Frankly, as the problem you are facing is originating from the <ui:repeat>
component/tag, doing process="example-form:example-container"
on your command buttons should also work just fine - just make sure <ui:repeat>
is included in the processing stage.