I have a set of p:accordionPanel's that I wish to update in my JSF 2.2 page.
The first one lists items of the list as tabs, in the tab it shows the name of the list item plus a button to delete that item. Clicking delete correctly removes the item and updates the panel via ajax
When all items have been removed it will render a new accordion panel informing the user there are no items and one needs to be added.
This all works fine, however when a new item is added is should once again render the first accordion panel showing the item with the option to remove it.
I believe the following is Minimal, Complete and Verifiable (if not please let me know why not)
The xHTML
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Forms</title>
</h:head>
<h:body>
<h:form id="mainForm">
<p:outputPanel id="panelContainer">
<p:accordionPanel id="formsPanel" value="#{formController.forms}"
var="formInstance" rendered="#{not empty formController.forms}">
<p:tab title="#{formInstance.name}">
<p:panelGrid columns="3" layout="grid">
<h:outputLabel for="name" value="Form name:" />
<h:outputText id="name" value="#{formInstance.name}" />
<p:commandButton
action="#{formController.deleteForm(formInstance.name)}"
value="Delete form" update=":mainForm:panelContainer" />
</p:panelGrid>
</p:tab>
</p:accordionPanel>
<p:accordionPanel id="addFormPanel"
rendered="#{empty formController.forms}">
<p:tab title="No forms added yet, please add some">
<h:form>
<p:panelGrid columns="3" layout="grid">
<h:outputLabel value="Form name:" />
<p:inputText value="#{formController.form.name}" required="true"
label="text" />
<p:commandButton update=":mainForm:panelContainer"
action="#{formController.createForm}" value="Add form" />
</p:panelGrid>
</h:form>
</p:tab>
</p:accordionPanel>
</p:outputPanel>
</h:form>
</h:body>
</html>
The Form
public class Form implements Serializable
{
private String name;
public Form()
{
}
public Form(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
And the FormController
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
#Named
#ViewScoped
public class FormController implements Serializable
{
private static List<Form> forms;
{
forms = new ArrayList<>();
forms.add(new Form("Form 1"));
forms.add(new Form("Form 2"));
}
private Form form = new Form();
public void createForm()
{
forms.add(form);
form = new Form();
}
public void deleteForm(String name)
{
for(int i=0; i < forms.size(); i++)
{
Form form = forms.get(i);
if (form.getName().equals(name))
{
forms.remove(i);
}
}
}
public List<Form> getForms()
{
return forms;
}
public Form getForm()
{
return form;
}
}
Related
I had a school project this past semester building a web application with a database for a section of Habitat for Humanity. I was able to connect and write to the database just fine, but I had trouble reading the data that was in the database and placing it into a Primefaces DataTable.
I will add my Java Class to read from the database below, as well as my HTML that has my variables for the DataTable.
If someone can take a look at my code and see if they find any mistakes or have any hints/tips, it would be much appreciated. The class is over now, but I want to be able to try and figure out the rest of the web application to get it fully up and running, instead of just leaving it in the past. Any help would be appreciated!
UserDAO - Java Class:
package dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import model.UserBean;
/**
* #author rnikolich
*/
public class UserDAO {
String driverName = "org.apache.derby.jdbc.ClientDriver";
String connStr = "jdbc:derby://localhost:1527/HabitatDatabase";
public static ArrayList<UserBean> getOwners() {
ArrayList<UserBean> ownerCollection = new ArrayList<>();
Connection DBConn = null;
try {
DBHelper.loadDriver("org.apache.derby.jdbc.ClientDriver");
String myDB = "jdbc:derby://localhost:1527/HabitatDatabase";
DBConn = DBHelper.connect2DB(myDB, "test", "test");
// UserBean usr = new UserBean();
String query = "SELECT * FROM HABITATDATABASE.OWNER";
// With the connection made, create a statement to talk to the DB
server.
// Create a SQL statement to query, retrieve the rows one by one (by
going to the
// columns), and formulate the result string to send back to the
client.
Statement stmt = DBConn.createStatement();
ResultSet rs = stmt.executeQuery(query);
// Timestamp timestamp;
// Vote aVote;
while (rs.next()) {
UserBean own = new UserBean();
own.setOwnerID(rs.getString("OwnerID"));
own.setOwnerName(rs.getString("OwnerName"));
own.setPhone(rs.getString("Phone"));
own.setEmail(rs.getString("Email"));
// or rs.getString("PL_Name");
// timestamp = rs.getTimestamp("vote_TimeStamp");
//
// // make a VoteTally object out of the values
// aVote = new Vote(PL_Name, timestamp);
// add the newly created object to the collection
ownerCollection.add(own);
}
rs.close();
stmt.close();
} catch (Exception e) {
System.err.println("ERROR: Problems with SQL select");
e.printStackTrace();
}
try {
DBConn.close();
} catch (SQLException e) {
System.err.println(e.getMessage());
}
return ownerCollection;
}
}
usersController - Java Class:
package controller;
import dao.UserDAO;
import java.util.ArrayList;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import model.UserBean;
import java.io.Serializable;
#ManagedBean
#SessionScoped
public class UsersController implements Serializable {
private UserBean theModel;
private ArrayList<UserBean> owner;
/**
* *
*/
public ArrayList<UserBean> getOwner() {
UserDAO own = new UserDAO();
owner = UserDAO.getOwners();
return owner;
}
public void setOwner(ArrayList<UserBean> owner) {
this.owner = owner;
}
/**
* Creates a new instance of UsersController
*/
public UsersController() {
theModel = new UserBean();
}
public UserBean getTheModel() {
return theModel;
}
public void setTheModel(UserBean theModel) {
this.theModel = theModel;
}
}
viewOwner - XHTML:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
<h:head>
<meta http-equiv="Content-type" content="text/html; charset=UTF-8" />
<title>View Owner</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"
integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></link>
</h:head>
<h:body style="background-color: gray">
<h:panelGroup layout="block" styleClass="container">
<h:panelGroup layout="block" styleClass="row centered-form">
<h:panelGroup layout="block" styleClass="col-xs-12 col-sm-8 col-md-10 col-sm-offset-2 col-md-offset-1"
style="background-color: limegreen">
<h:panelGroup layout="block" styleClass="panel panel-default">
<center><h:graphicImage value="#{resource['images:habitat-for-humanity-logo.jpg']}" width="200" height="180"/></center>
<h:panelGroup layout="block" styleClass="panel-heading">
<center><h3 class="panel-title">View Owner</h3></center>
</h:panelGroup>
<br></br>
<h:panelGroup layout="block" styleClass="panel-body">
<h:form id="viewOwnerForm">
<h:panelGroup layout="block" styleClass="form-group">
<p:growl id="msgs" showDetail="true" />
<p:dataTable id="viewOwnerDataTable" value="#{usersController.owner}" var="o" rows="10">
<f:facet name="header">
View Owners Table
</f:facet>
<p:column headerText="OwnerID">
<h:outputText value="#{o.ownerID}" />
</p:column>
<p:column headerText="OwnerName">
<h:outputText value="#{o.ownerName}" />
</p:column>
<p:column headerText="Phone">
<h:outputText value="#{o.phone}" />
</p:column>
<p:column headerText="Email">
<h:outputText value="#{o.email}" />
</p:column>
</p:dataTable>
<br></br>
<h:commandLink>
<p:graphicImage value="#{resource['images:pdf.png']}" width="24"/>
<p:dataExporter type="pdf" target="tbl" fileName="owners"/>
</h:commandLink>
<br></br>
<br></br>
<h:link outcome="home" value="Home" styleClass="btn btn-warning btn-block" id="Home" style="background-color: blue"/>
<h:link outcome="viewHome" value="View Entry Home" styleClass="btn btn-warning btn-block" id="viewHome" style="background-color: blue"/>
</h:panelGroup>
</h:form>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</h:panelGroup>
</h:body>
</html>
I have two dropdowns: One is implemented using a selectOneMenu component and the other is implemented with selectCheckboxMenu.
When selecting an option in the selectOneMenu, the option values of the selectCheckboxMenu are updated depending of what option was selected in the selectOneMenu.
The following scenario happens:
I select an option of the selectOneMenu, the selectCheckboxMenu gets populated
I select/check some options of the selectCheckboxMenu
I select another option of the selectOneMenu, the selectCheckboxMenu gets populated with other values
I select some of the new values
I select the option in step 1 of the selectOneMenu
The values I selected in step 2 are no longer selected
I believe this is because the list value bound to the selectCheckboxMenu is getting reset by the setter method.
What I would like is for the state of the selectCheckboxMenu to be globally saved. What would be the best strategy for doing this?
EDIT:
Here's the relevant code that duplicates the previous behavior:
Bean:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
#ManagedBean
#ViewScoped
public class BackingBean {
private List<Parent> parents = new ArrayList<>();
private List<Child> children = new ArrayList<>();
private List<Child> selectedChildren = new ArrayList<>();
private Parent selectedParent = new Parent();
#Inject
FamilyService service;
#PostConstruct
public void init(){
parents = service.findParents();
}
public void parentChanged(){
children = new ArrayList<>();
if(getSelectedParent() == null){
return;
}
children = service.findChildrenByParent(getSelectedParent());
}
public void selectedSonChanged(){
System.out.println(selectedChildren.size());
}
public List<Parent> getParents() {
return parents;
}
public void setParents(List<Parent> parents) {
this.parents = parents;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
public List<Child> getSelectedChildren() {
return selectedChildren;
}
public void setSelectedChildren(List<Child> selectedChildren) {
this.selectedChildren = selectedChildren;
}
public Parent getSelectedParent() {
return selectedParent;
}
public void setSelectedParent(Parent selectedParent) {
this.selectedParent = selectedParent;
}
}
XHTML:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<h:form id="selectOne">
<p:messages autoUpdate="true" />
<h:outputLabel for="parents" value="Parents:" />
<p:selectOneMenu converter="#{parentConverter}" id="parents" value="#{backingBean.selectedParent}">
<p:ajax update="children" listener="#{backingBean.parentChanged}" />
<f:selectItem noSelectionOption="true" value="#{null}" itemLabel="None" />
<f:selectItems value="#{backingBean.parents}" var="p" itemLabel="#{p.name}" itemValue="#{p}" />
</p:selectOneMenu>
<h:outputLabel for="children" value="Children:" />
<p:selectCheckboxMenu converter="#{childConverter}" id="children"
value="#{backingBean.selectedChildren}"
label="Children" filter="true" filterMatchMode="startsWith">
<p:ajax update="display" listener="#{backingBean.selectedSonChanged}" />
<f:selectItems value="#{backingBean.children}" var="c" itemLabel="#{c.name}" itemValue="#{c}" />
</p:selectCheckboxMenu>
<p:outputPanel id="display">
<p:dataList value="#{backingBean.selectedChildren}" var="sn" emptyMessage="No children selected">
#{sn.name}
</p:dataList>
</p:outputPanel>
<p:commandButton value="Submit" update="display, selectOne" />
</h:form>
</h:body>
</html>
The complete project is here: https://github.com/cenobyte321/jsfsamples/tree/master/selectcheckboxmenu
You can find a video of the interaction mentioned previously here:
https://github.com/cenobyte321/jsfsamples/blob/master/selectcheckboxmenu/example1.mp4?raw=true
As you can see when choosing another "Parent" and then selecting their children the previous children list gets substituted. How can I properly modify this behavior so the previously selected elements get persisted and shown as checked in the selectCheckboxMenu?
EDIT 2:
One solution I found was introducing another variable which will hold the global values and will be modified in the listener method "selectedSonChanged". Here's the relevant code:
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
#ManagedBean
#ViewScoped
public class BackingBean {
private List<Parent> parents = new ArrayList<>();
private List<Child> children = new ArrayList<>();
private List<Child> selectedChildren = new ArrayList<>();
private List<Child> globalSelectedChildren = new ArrayList<>();
private Parent selectedParent = new Parent();
#Inject
FamilyService service;
#PostConstruct
public void init(){
parents = service.findParents();
}
public void parentChanged(){
children = new ArrayList<>();
if(getSelectedParent() == null){
return;
}
children = service.findChildrenByParent(getSelectedParent());
selectedChildren = globalSelectedChildren.stream().filter(c -> c.getParent().equals(selectedParent)).collect(Collectors.toList());
System.out.println(selectedChildren.size());
}
public void selectedSonChanged(){
System.out.println(selectedChildren.size());
globalSelectedChildren = globalSelectedChildren.stream().filter(c -> !c.getParent().equals(selectedParent)).collect(Collectors.toList());
globalSelectedChildren.addAll(selectedChildren);
}
public List<Parent> getParents() {
return parents;
}
public void setParents(List<Parent> parents) {
this.parents = parents;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
public List<Child> getSelectedChildren() {
return selectedChildren;
}
public void setSelectedChildren(List<Child> selectedChildren) {
this.selectedChildren = selectedChildren;
}
public Parent getSelectedParent() {
return selectedParent;
}
public void setSelectedParent(Parent selectedParent) {
this.selectedParent = selectedParent;
}
public List<Child> getGlobalSelectedChildren() {
return globalSelectedChildren;
}
public void setGlobalSelectedChildren(List<Child> globalSelectedChildren) {
this.globalSelectedChildren = globalSelectedChildren;
}
}
XHTML:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head></h:head>
<h:body>
<h:form id="selectOne">
<p:messages autoUpdate="true" />
<h:outputLabel for="parents" value="Parents:" />
<p:selectOneMenu converter="#{parentConverter}" id="parents" value="#{backingBean.selectedParent}">
<p:ajax update="children" listener="#{backingBean.parentChanged}" />
<f:selectItem noSelectionOption="true" value="#{null}" itemLabel="None" />
<f:selectItems value="#{backingBean.parents}" var="p" itemLabel="#{p.name}" itemValue="#{p}" />
</p:selectOneMenu>
<h:outputLabel for="children" value="Children:" />
<p:selectCheckboxMenu converter="#{childConverter}" id="children"
value="#{backingBean.selectedChildren}"
label="Children" filter="true" filterMatchMode="startsWith">
<p:ajax update="display" listener="#{backingBean.selectedSonChanged}" />
<f:selectItems value="#{backingBean.children}" var="c" itemLabel="#{c.name}" itemValue="#{c}" />
</p:selectCheckboxMenu>
<p:outputPanel id="display">
<p:dataList value="#{backingBean.globalSelectedChildren}" var="sn" emptyMessage="No children selected">
#{sn.name}
</p:dataList>
</p:outputPanel>
<p:commandButton value="Submit" update="display, selectOne" />
</h:form>
</h:body>
</html>
Here's the branch with this solution: https://github.com/cenobyte321/jsfsamples/tree/solution-1/selectcheckboxmenu
I am basing my example on the PrimeFaces showcase example for the p:dataTableContextMenu example
The difference being I am trying to delete via a p:confirmDialog but the selected item is always null.
Here's a cut down example
The XHTML
<!DOCTYPE html>
<html xmlns="http://www.w3c.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Example</title>s
</h:head>
<h:body>
<h:form>
<p:confirmDialog widgetVar="cd" severity="alert" header="Confirmation"
message="Are you sure you wish to delete this car?">
<p:commandButton value="Yes" action="#{carTableView.deleteCar}"
update=":dataForm" oncomplete="PF('cd').hide();" />
<p:commandButton value="No" onclick="PF('cd').hide();" type="button" />
</p:confirmDialog>
</h:form>
<h:form id="dataForm">
<p:contextMenu for="cars">
<p:menuitem value="Delete" icon="ui-icon-close"
onclick="PF('cd').show(); return false;" />
<!-- action="#{formsView.deleteForm}" update=":dataForm" /> -->
</p:contextMenu>
<p:dataTable id="cars" var="car" value="#{carTableView.cars}"
rowKey="#{car.id}" selection="#{carTableView.selectedCar}"
selectionMode="single">
<f:facet name="header">
RightClick to View Options
</f:facet>
<p:column headerText="Id">
<h:outputText value="#{car.id}" />
</p:column>
<p:column headerText="Name">
<h:outputText value="#{car.name}" />
</p:column>
</p:dataTable>
</h:form>
</h:body>
</html>
The Model
public class Car
{
private String id;
private String name;
public Car(String id, String name)
{
this.id = id;
this.name = name;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
And the bean
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#SuppressWarnings("serial")
#ManagedBean
#ViewScoped
public class CarTableView implements Serializable
{
private List<Car> cars;
private Car selectedCar;
#PostConstruct
public void init()
{
cars = createCars();
}
private List<Car> createCars()
{
List<Car> list = new ArrayList<Car>();
for (int i = 0; i < 10; i++)
{
list.add(new Car(UUID.randomUUID().toString().substring(0, 8), "Car " + String.valueOf(i + 1)));
}
return list;
}
public void deleteCar()
{
cars.remove(selectedCar);
selectedCar = null;
}
public List<Car> getCars()
{
return cars;
}
public void setCars(List<Car> cars)
{
this.cars = cars;
}
public Car getSelectedCar()
{
return selectedCar;
}
public void setSelectedCar(Car selectedCar)
{
this.selectedCar = selectedCar;
}
}
Now it seems to me that it's the involvement of running the deleteCar action from the p:confirmDialog that is the issue.
I say this as if I change
<p:menuitem value="Delete" icon="ui-icon-close"
onclick="PF('cd').show(); return false;" />
To
<p:menuitem value="Delete" icon="ui-icon-close"
action="#{formsView.deleteForm}" update=":dataForm" />
Then it works. In the p:confirmDialog example the selectedCar in the deleteCar method is always null. Despite specifying a rowKey attribute in p:dataTable
Since you have two forms, enclose <h:setPropertyActionListener > to your <p:menuitem>
Answer: The p:confirmDialog needs to be part of the same h:form as the p:dataTable
I try to get the information about the clicked button in a <p:datalist>, but it doesn't work.
My View:
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.org/ui" template="/WEB-INF/template.xhtml">
<ui:define name="head">
<title>Hash Generator</title>
</ui:define>
<ui:define name="content">
<h:form id="hashForm">
<p:dataList id="hashList" value="#{hashGeneratorBean.hashList}" var="entry" rowIndexVar="idx" itemType="none">
<p:column>
<h:outputText value="#{idx + 1}" />
<h:inputText value="#{entry.clearText}" />
<h:inputText value="#{entry.hashedText}" readonly="true" disabled="true" size="#{entry.hashedText.length() + 15}"/>
<p:commandButton id="addRow" actionListener="#{hashGeneratorBean.addRow}" icon="ui-icon-plus" title="Icon Only" update="hashList">
<f:setPropertyActionListener value="#{entry}" target="#{hashGeneratorBean.selectedRow}" />
</p:commandButton>
<p:commandButton id="debugBtn" icon="ui-icon-disc" title="Icon Only" update=":hashForm:display" oncomplete="PF('dlg').show()">
<f:setPropertyActionListener value="#{entry}" target="#{hashGeneratorBean.selectedRow}" />
</p:commandButton>
</p:column>
</p:dataList>
<p:commandButton actionListener="#{hashGeneratorBean.hash}" value="Generate Hashes" update="hashList" />
<p:dialog modal="true" widgetVar="dlg">
<h:panelGrid id="display" columns="2">
<f:facet name="header">
<h:outputText value="#{hashGeneratorBean.selectedRow.clearText}" />
</f:facet>
<h:outputText value="#{hashGeneratorBean.selectedRow.hashedText}" />
</h:panelGrid>
</p:dialog>
</h:form>
</ui:define>
</ui:composition>
My Controller:
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.ActionEvent;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
#ManagedBean
#ViewScoped
public class HashGeneratorBean {
private List<HashDTO> hashList = new ArrayList<HashDTO>();
private HashDTO selectedRow = new HashDTO();
#PostConstruct
public void init() {
hashList.add(new HashDTO());
}
public void addRow(ActionEvent ae){
hashList.add(new HashDTO());
}
public void hash(ActionEvent ae){
for (HashDTO entry : hashList){
entry.setHashedText(generateHash(entry.getClearText()));
}
}
/**
* Hashes the given password with SHA-256
* #param password
* #return passwordHash
*/
public static String generateHash(String password) {
return Hashing.sha256().hashString(password, Charsets.UTF_8).toString();
}
public List<HashDTO> getHashList() {
return hashList;
}
public void setHashList(List<HashDTO> hashList) {
this.hashList = hashList;
}"
public HashDTO getSelectedRow() {
return selectedRow;
}
public void setSelectedRow(HashDTO selectedRow) {
this.selectedRow = selectedRow;
}
}
If I click the "debugBtn"-button the dialog popups up and shows the correct information about the row. But If I click the "addRow"-button the data in the managed-bean isn't filled correct. The selectedRow-property allways stores the last added row from the hashList-property.
I found the solution.
The PropertyActionListener is called after the ActionListener.
the Solution is to use "Action" or register the the ActionListener with and a Extended Action Listener
If I run the app and click the first p:commandLink, the item gets set as expected. When clicking another p:commandLink the previous one will be removed (null). The h:commandLink does not have that problem.
If I add :theform:output to the update parameter of the p:commandLink, the previously clicked item stops disappearing.
What is the reason for this behaviour?
Env
Java 1.7.45
Netbeans 7.4
JSF Mojarra 2.2.4
Primefaces 4.0
Index.xhtml
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Test</title>
</h:head>
<h:body>
<h:form id="theform">
<h:panelGroup layout="block" styleClass="tableBorder" id="tableWrapper">
<h:dataTable value="#{xtestListBean.list}" var="item">
<h:column>
<h:outputText value="#" styleClass="tableItem" />
</h:column>
<h:column>
<h:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}">
<f:ajax render=":theform:tableWrapper" />
</h:commandLink>
<p:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}"
update=":theform:tableWrapper">
</p:commandLink>
</h:column>
</h:dataTable>
</h:panelGroup>
<h:inputText value="#{xtestEntityBean.item.name}" size="40" id="output" />
</h:form>
</h:body>
</html>
XtestListBean.java
package beans;
import dto.Item;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean
#ViewScoped
public class XtestListBean implements Serializable {
private List<Item> list;
public XtestListBean() {
list = new ArrayList();
list.add(new Item("Test 1"));
list.add(new Item("Test 2"));
list.add(new Item("Test 3"));
list.add(new Item("Test 4"));
list.add(new Item("Test 5"));
list.add(new Item("Test 6"));
list.add(new Item("Test 7"));
}
public List<Item> getList() {
return list;
}
}
XtestEntityBean.java
package beans;
import dto.Item;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#ViewScoped
public class XtestEntityBean implements Serializable {
private Item item = new Item();
public XtestEntityBean() {
}
public void setItem(Item item) {
this.item = item;
}
public Item getItem() {
return item;
}
public String getVersion() {
return FacesContext.class.getPackage().getImplementationTitle()
+ " version "
+ FacesContext.class.getPackage().getImplementationVersion();
}
}
That only looks like so. It's just that the <h:inputText value="#{xtestEntityBean.item.name}"> is also processed when the <p:commandLink> is invoked. In effects, the item's name is submitted with an empty/null value and that get reflected in the table. It's not the whole Item instance which became null or so.
How is this caused? Well, the <f:ajax execute> defaults to #this, meaning that only the current component (i.e. the command link) is processed during the form submit. However, the PrimeFaces equivalent, <p:commandLink process> defaults to #form, meaning that the entire parent form is processed during form submit, including that empty input field referring the item name.
If you explicitly set <p:commandLink process> to #this, the same as <f:ajax execute>'s default, then it should work as intented.
<p:commandLink
value="#{item.name}"
actionListener="#{xtestEntityBean.setItem(item)}"
process="#this" update=":theform:tableWrapper">
</p:commandLink>