If I have a dataTable which is controlled by a requestScoped named bean, the named bean(backing bean) query data from EJB and EJB query data via entityManager and JPA.
For the view part, I use the dataTable from PrimeFaces: My question is: If I open two tab in one browser or two(problem is same): I delete one row in one tab and then go to another tab which is not refreshed(so the deleted row is still there). If I press delete commandLink again, the delete row will of course be deleted because the page is refreshed but the problem is that it also deletes the row right below it. By the way, my JSF version is 2.2 and primefaces is 4.0(now I replace it with 8.0 and the error persists). Is it a jsf or primefaces bug(I never try the h:commandLink and h:dataTable) or some mistake I made?
You can reference below code for named bean:
@Named(value="allIndustryController")
@RequestScoped
public class AllIndustryController {
@Inject
IndustryTypeBean industryTypeBean;
private List<Industry> industries;
public AllIndustryController() {
}
@PostConstruct
public void init() {
industries = industryTypeBean.getAllIndustrys();
}
public List<Industry> getIndustries() {
return industries;
}
public void setIndustries(List<Industry> industries) {
this.industries = industries;
}
public String deleteIndustry(int industryID) {
try {
boolean result = industryTypeBean.removeIndustry(industryID);
if (result) {
init();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Industry " + industryID +" has been deleted succesfully"));
}
}catch(Exception ex){
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Delete Industry Failed"));
}
return "allIndustries.xhtml?faces-redirect=true";
}
public void updateIndustry(int industryID, String industryName) {
Industry industry = industryTypeBean.findIndustryByID(industryID);
industry.setIndustryName(industryName);
industryTypeBean.editIndustry(industry);
}
public String addIndustry() {
Industry industry = new Industry();
industryTypeBean.addIndustry(industry);
return "allIndustries.xhtml?faces-redirect=true";
}
}
The code for dataTable:
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>All Industries</title>
<h:outputStylesheet library="css" name="bootstrap.min.css"></h:outputStylesheet>
</h:head>
<body>
<div class="navbar navbar-expand-lg navbar-light bg-light">
<div class="navbar-brand">Customer Management System</div>
<div class="collapse navbar-collapse">
<ul class="navbar-nav mr-auto">
<li class="nav-item active"><h:link class="nav-link"
value="Home | " outcome="index.xhtml" /></li>
<li class="nav-item"><h:link class="nav-link"
value="Manage All Users | " outcome="allUsers.xhtml" /></li>
<li class="nav-item"><h:link class="nav-link"
value="Manage All Customers |" outcome="allCustomers.xhtml" /></li>
<li class="nav-item"><h:link class="nav-link"
value="Manage Industries |" outcome="allIndustries.xhtml" /></li>
<h:form>
<li class="nav-item"><h:commandLink class="nav-link"
value="Log out" action="#{loginController.logout()}" /></li>
</h:form>
</ul>
</div>
</div>
<h:form id="MyForm">
<p:dataTable class="table table-bordered table-striped"
id="myIndustryList" value="#{allIndustryController.industries}"
var="industry">
<p:column headerText="industryID">
<p:outputLabel value="#{industry.industryID}" id="industryID" />
</p:column>
<p:column headerText="industryName">
<p:inputText value="#{industry.industryName}" id="industryName" />
</p:column>
<p:column headerText="Operations">
<p:commandLink value="Delete | " ajax="true"
action="#{allIndustryController.deleteIndustry(industry.industryID)}"
disabled="#{industry.industryID == null}" update="myIndustryList">
<p:confirm header="Confirmation" message="Are you sure?"
icon="pi pi-exclamation-triangle" />
</p:commandLink>
<p:commandButton value="update Name"
action="#{allIndustryController.updateIndustry(industry.industryID,industry.industryName)}"
oncomplete="PF('cd').show()">
<!-- <f:param name="userAccount" value="#{normalUser.account}" /> -->
</p:commandButton>
</p:column>
<!-- <h:link value="View | " outcome="userDetail.xhtml">
pass the parameter to next page, the param name is propertyID, and the value is index + 1.
You can get the value from next page using the indexController
<f:param name="userAccount" value="#{normalUser.account}" />
</h:link>
<h:link value="Edit | " outcome="editUser.xhtml">
pass the parameter to next page, the param name is propertyID, and the value is index + 1.
You can get the value from next page using the indexController
<f:param name="userAccount" value="#{normalUser.account}" />
</h:link> -->
<!-- reference:https://stackoverflow.com/questions/19362983/how-to-add-confirmation-dialog ; https://www.primefaces.org/showcase/ui/data/datatable/basic.xhtml-->
</p:dataTable>
<p:confirmDialog global="true" showEffect="fade" hideEffect="fade">
<p:commandButton value="Yes" type="button"
styleClass="ui-confirmdialog-yes" icon="pi pi-check" />
<p:commandButton value="No" type="button"
styleClass="ui-confirmdialog-no" icon="pi pi-times" />
</p:confirmDialog>
<h:outputText value=" " />
<p:dialog header="Save the name" severity="alert" widgetVar="cd">
<h:outputText value="The Industry Name is saved" />
</p:dialog>
<div>
<h:commandButton id="add" value="Add"
action="#{allIndustryController.addIndustry()}"
class="btn btn-primary" />
<h:commandButton id="viewAll" value="View All"
action="allIndustries.xhtml?faces-redirect=true"
class="btn btn-primary">
</h:commandButton>
</div>
<!-- <div>
<h:inputText value="#{adminApplication.searchedAccount}"
id="searchByAccount"
onchange="if (document.getElementById('MyForm:searchByAccount').value.trim() == '') {document.getElementById('MyForm:searchByAccount').value = '';} " />
<h:commandButton id="search" value="Search by Account"
action="#{adminApplication.searchUserByAccount(adminApplication.searchedAccount)}"
class="btn btn-primary">
</h:commandButton>
</div> -->
</h:form>
</body>
</html>
------------------------------Update-------------------------------
Actually I solve this problem by using <f:param> to check againt the one passed by named bean's method. I forgot the update it due to the heavy assignments. (Sorry Moreover, If I use some search feature on the page(I filter the ArrayList which stores the industry), the delete action can neither be performed on the correct the row:
I guess I can summarize the problem as below: Whenever I update the actual dataTable's data source in backing bean, I cannot pass parameter(the parameter will not be consistent and it seems to be a index problem? just a wild guess) inside a method of backing bean. Anyway, f:param can alway give the right one
For more people who are interested, I post the IndustryType.java code though it is not related to the problem. The problem is still about how the primefaces process the xhtml files. Welcome to more detailed explanation and hope this post can be useful to more people :
package beans;
import java.io.Serializable;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import entity.Industry;
import repository.IndustryRepository;
@Named
@SessionScoped
public class IndustryTypeBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@EJB
IndustryRepository industryRepository;
public List<Industry> getAllIndustrys(){
try {
List<Industry> industrys = industryRepository.getAllIndustries();
return industrys;
}catch (Exception ex) {
Logger.getLogger(IndustryTypeBean.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
public boolean addIndustry(Industry Industry) {
try {
industryRepository.addIndustry(Industry);
return true;
}catch(Exception ex) {
Logger.getLogger(IndustryTypeBean.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public boolean removeIndustry(int industryID) {
try {
industryRepository.deleteIndustry(industryID);
return true;
} catch(Exception ex) {
Logger.getLogger(IndustryTypeBean.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public boolean editIndustry(Industry Industry) {
try {
industryRepository.updateIndustry(Industry);
return true;
}catch(Exception ex) {
Logger.getLogger(IndustryTypeBean.class.getName()).log(Level.SEVERE, null, ex);
}
return false;
}
public Industry findIndustryByID (int industryID) {
try {
return industryRepository.findIndustryByID(industryID);
}catch(Exception ex) {
Logger.getLogger(IndustryTypeBean.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
}
In JSF, there is CDI for viewscope and flowscope so that you have to use other implementation.