b:commandButton inside dataTable won't fire action method - jsf-2

I just started using BootsFaces because of client's demand, so bear with me.
Here's my xhtml:
<h:form id="form" prependId="false">
<b:dataTable id="dtEmpresas" value="#{recController.empresas}" var="e" searching="false" striped="true"
styleClass="tabela" customLangUrl="../resources/js/lang-pt-br.json" paginated="false" pageLength="6" >
<b:dataTableColumn header-style-class="hidden" styleClass="col-md-4">
<b:selectOneMenu id="congenere" value="#{e.congenere.id}">
<!-- required="true" requiredMessage="Congenere é obrigatório"> -->
<f:selectItem noSelectionOption="true" itemLabel="[Selecione...]"
itemValue="#{null}" />
<f:selectItems value="#{recController.congeneres}" var="c"
itemLabel="#{c.nome}" itemValue="#{c.id}" />
</b:selectOneMenu>
</b:dataTableColumn>
<b:dataTableColumn styleClass="col-md-4" header-style-class="hidden">
<b:inputText id="nomeEmpresa" value="#{e.nomeEmpresa}" size="50" disabled="#{!e.ehOutros()}" />
</b:dataTableColumn>
<b:dataTableColumn styleClass="col-md-4" header-style-class="hidden">
<b:row>
<b:column styleClass="col-md-10">
<b:inputText value="#{e.cnpj}" size="50" required="false"
validatorMessage="CNPJ invalido 21" placeholder="CNPJ Recolhimento">
<f:validator validatorId="cnpjValidador" />
</b:inputText>
</b:column>
<ui:debug hotkey="j" />
<b:commandButton value=" - " action="#{recController.removeItem(e)}" update="dtEmpresas" />
<b:commandButton id="btAdd" value=" + " action="#{recController.addItem()}"
update="dtEmpresas" process="dtEmpresas" />
</b:row>
</b:dataTableColumn>
</b:dataTable>
None of those buttons fire their's method in backing bean. But if I move then outside of dataTable, they work just fine.
Here's the back bean, running on WildFly 8 with Deltaspike:
#ViewScoped
#ManagedBean
public class RecController implements Serializable{
private static final long serialVersionUID = 1L;
private List<EmpresaRecolheInss> empresas = new ArrayList<>();
#Inject
private BaseDAO dao;
#PostConstruct
public void init(){
congeneres = dao.listar(Congenere.class);
empresas.add(new EmpresaRecolheInss());
}
public void salvar(){
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_INFO, "Salvo", null);
FacesContext.getCurrentInstance().addMessage(null, message);
}
public void addItem(){
empresas.add(new EmpresaRecolheInss());
}
public void removeItem(Empresa eri){
empresas.remove(eri);
}
Any ideas?

That's a bug of the BootsFaces 0.9.1 datatable I've repaired last weekend. Also see https://github.com/TheCoder4eu/BootsFaces-OSP/issues/486. The bug will be solved with BootsFaces 1.0. Until then, you can use the BootsFaces-1.0.0 developer snapshot on Maven Central as long as you're aware it's not meant to be used in production. See https://github.com/TheCoder4eu/BootsFaces-OSP/issues/369 on how to get the "sneak preview" version.

Related

update component in another xhtml form from Custom layout p:selectOneRadio not working

My xhtml is split in to Menu area (defaultMenu.xhtml) and Content area (defaultContent.xhtml).
The code for defaultMenu.xhtml is:
<h:form id="defaultmenuform">
<p:outputPanel id="menupanel" class="contain auto-fixed-center">
<p:panel id="pmenu" visible="#{phController.user.menuVisible}">
<table id="stutable">
<tr>
<td width="15%">
<p:outputLabel id="stuname" value="#{phController.phBean.studentName}" />
</td>
<td>
<p:tabMenu activeIndex="#{param.selectedtab}">
<p:menuitem value="Home" outcome="phome" icon="ui-icon-star">
<f:param name="selectedtab" value="0" />
</p:menuitem>
<p:menuitem value="Bank" outcome="bhome" icon="ui-icon-person">
<f:param name="selectedtab" value="1" />
</p:menuitem>
</p:tabMenu>
</td>
</tr>
</table>
</p:panel>
</p:outputPanel>
</h:form>
The defaultContent.xhtml actually displays the ph.xhtml content (as part of functional navigation) and the code is:
<ui:define name="content">
<f:event listener="#{phController.readPeople}" type="preRenderView">
</f:event>
<h:form id="form">
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
<p:ajax update=":defaultmenuform:parentmenupanel :defaultmenuform:stuname" listener="#{phController.onChangePerson}"/>
<f:selectItems value="#{phController.selectStudents}" />
</p:selectOneRadio>
<div style="width: 300px; float:left;">
<p:dataGrid var="studentlist" value="#{phController.listStudent}" columns="1" rowIndexVar="stuindex">
<p:panel header="" style="text-align:left">
<h:panelGrid columns="1" style="width:100%">
<h:outputText value="#{studentlist.studentName}" />
<p:radioButton for=":form:selstud" itemIndex="#{stuindex}"/> Select
</h:panelGrid>
</p:panel>
</p:dataGrid>
</div>
</h:form>
</ui:define>
The code for backing bean is:
Map<String, Object> studentparam = new HashMap<>();
studentparam.put("studentSeq", phBean.getSsSeq());
lS = getBaseDAOService().readStudent("readStudent", studentparam);
phBean.setStudentName(lS.get(0).getStudentFirstName() + " " + lS.get(0).getStudentLastName());
As you can see, I am calling the onChangeStu method to display the selected Student Name in defaultMenu.xhtml. I am using Custom Layout p:selectOneRadio in ph.xhtml and onClick trying to update a p:outputLabel in defaultMenu.xhtml.
The backing bean method gets invoked successfully and the value is also set in variable phController.phBean.studentName, but the update is not working. I also checked using view source and the id is “:defaultmenuform:stuname”, I also tried updating the menu panel ":defaultmenuform:menupanel”, but none of this works.
Not sure how to resolve this. Please suggest.
Including the structure of all .xhtmls
<h:body id="entirePageBody">
<div id="page">
<ui:insert name="header" >
<ui:include src="/template/defaultHeader.xhtml" />
</ui:insert>
<ui:insert name="menu" >
<ui:include src="/template/defaultMenu.xhtml" />
</ui:insert>
<div id="content_div" class="auto-fixed-center">
<div id="content_div_padding" class="content-block">
<ui:insert name="content" >
<ui:include src="/template/defaultContent.xhtml" />
<ui:debug hotkey="z" />
</ui:insert>
</div>
</div>
<ui:insert name="footer" >
<ui:include src="/template/defaultFooter.xhtml" />
</ui:insert>
</div>
</h:body>
PhController.java:
public class PhController extends BaseController implements Serializable {
private List<Stud> listStudent;
private List selectStudents;
SelectItem option;
private PhBean phBean;
private Boolean menuVisible;
int counter = 0;
public PhController() {
phBean = new PhBean();
}
public void readPeople() {
listStudent = new ArrayList<Stud>();
listStudent.add(new Stud(1, "John Miller"));
listStudent.add(new Stud(2, "Scott Jackson"));
selectStudents = new ArrayList();
option = new SelectItem(listStudent.get(0).getStudentSeq(), "Select");
selectStudents.add(option);
option = new SelectItem(listStudent.get(1).getStudentSeq(), "Select");
selectStudents.add(option);
phBean.setSsSeq(String.valueOf(1));
phBean.setSelectedName(listStudent.get(0).getStudentName());
menuVisible = true;
}
public void onChangePerson() {
phBean.setSelectedName(listStudent.get(1).getStudentName());
}
// Getters and Setters
}
PhBean.java:
public class PhBean implements Serializable {
private String ssSeq;
private String studName; // Used to display the name in the Menu bar.
private String selectedName;
public PhBean() {
}
// Getters and Setters
}
I'd say that in the p:ajax in defaultContent.xhtml the list of components to be updated should be separated with spaces only, no commas - so try changing this:
update=":defaultmenuform:menupanel, :defaultmenuform:stuname"
to this:
update=":defaultmenuform:menupanel :defaultmenuform:stuname"
UPDATE
I played with this a bit more and may have found a clue - please add the following code to defaultmenuform:
<p:messages autoUpdate="true" showDetail="true" />
This should help us tracking the reason for failed validation (in case failing validation is the root cause for you - as I said, I have rather limited possibility to reproduce this issue).
Anyway, when I selected some item in p:selectOneRadio, an error message like this appeared:
Conversion Error setting value 'test001.Student#4110c95c' for 'null Converter'.
And the root cause was on this row:
<p:selectOneRadio id="selstud" value="#{phController.phBean.ssSeq}" layout="custom">
p:selectOneRadio expects only String to be passed as a value - and ssSeq is very likely of a different type. Try to change the way value is populated to ensure it is always String - maybe a different attribute of the phBean or simply a brand new String one.
NOTE: if this doesn't help, maybe you could update your question with very simplified example of how phController and phBean could look like if we are to test it.
UPDATE #2
So you have explained there is a problem that you want to call phController.readPeople every time the page is loaded/refreshed, but instead it gets loaded with each and every Ajax request, thus overwriting the values.
In your PhController (it is a bean, right? session scoped?) you could add something like this (omitted null checks for the sake of readability):
public void readPeopleOnGet() {
FacesContext fc = FacesContext.getCurrentInstance();
ExternalContext ec = fc.getExternalContext();
HttpServletRequest req = (HttpServletRequest) ec.getRequest();
String reqMethod = req.getMethod();
if ("GET".equals(reqMethod)) {
readPeople();
}
}
With the above method you could keep this part of your defaultContext.xhtml in place, provided it is actually called (I assume so), just with the listener method changed:
<f:event listener="#{phController.readPeopleOnGet}" type="preRenderView">
</f:event>
The method readPeopleOnGet will still be called with every request to the page, but since Ajax requests are POST, it will only call readPeople when the page is loaded or refreshed as whole.
This may not be a "perfectly clean" solution, but seemed to work properly when I tested it.
UPDATE #3
Well, since you use PrimeFaces, it would be possible to identify Ajax call also this way:
public void readPeopleOnGet() {
RequestContext rc = RequestContext.getCurrentInstance();
if (!rc.isAjaxRequest()) {
readPeople();
}
}
But if I got your point from latest comments, you want to only run readPeople when the page is loaded for the very first time - so the following part could be even better for that.
You didn't answer if PhController is actually a bean, but I assume it is (there were no annotations visible from the code you posted). You may try making it #SessionScoped:
#ManagedBean
#SessionScoped
public class PhController extends BaseController implements Serializable {
// the rest of the PhController goes here...
Then you could add something like this to the PhController:
#PostConstruct
private void init() {
readPeople();
}
The annotation #PostConstruct ensures the method init is called exactly once after the bean was created. You can continue calling the method readPeople from other places as necessary, while removing both the <f:event ... /> and readPeopleOnGet as these will no longer be needed.

Save and update on same xhtml is not working in primefaces

Hi i tried the following functionality using primefaces.
In my xhtml i have a input field which takes input and saves into db.After completion of save i display the saved data in data table of primefaces.
Upto now works fine.now i add single selection to datatable .when user click on the row i update the same input column with selected data.
For this i use the following code
<h:form id="form">
<p:growl id="messages" />
<p:panel id="panel" header="Currency">
<h:panelGrid columns="2" id="currencyGrid">
<p:outputLabel value="Currency :"/>
<p:inputText value="#{currencyBean.currencyMaster.currDesc}"/>
</h:panelGrid>
<p:commandButton value="Add" action="#{currencyBean.saveCurrency}"
update="currencytable">
</p:commandButton>
<p:dataTable id="currencytable" var="cur"
value="#{currencyBean.currencyList}" widgetVar="dlg"
selection="#{currencyBean.currencyMaster}"
selectionMode="single" rowKey="#{cur.currId}">
<p:ajax event="rowSelect" listener="#{currencyBean.onRowSelect}"
update=":form:currencyGrid" />
<p:column headerText="ID" style="font-size:12px;">
<h:outputText value="#{cur.currId}" />
</p:column>
<p:column headerText="Description" style="font-size:12px;">
<h:outputText value="#{cur.currDesc}" />
</p:column>
</p:dataTable>
</h:form>
And My Bean code is:
#managedBean
#viewScoped
#component
public class CurrencyBean implements Serializable{
private static final long serialVersionUID = 1L;
#Autowired
private CurrencyMaster currencyMaster;
private List<CurrencyMaster> currencyList;
public CurrencyMaster getCurrencyMaster() {
return currencyMaster;
}
public void setCurrencyMaster(CurrencyMaster currencyMaster) {
this.currencyMaster = currencyMaster;
}
public void saveCurrency(){
try{
currencyService.saveCurrency(currencyMaster);
}catch(Exception e){
e.printStackTrace();
}
}
#PostConstruct
public void currencyList(){
setCurrencyList(currencyService.getCurrencyList());
}
public void onRowSelect(SelectEvent event) {}
public void onRowUnselect(UnselectEvent event) {}
}
So when i click on currrencytable row the input field populated properly and update operation also.
The problem is when i directly enter the data into input column my globalMaster property is going to be null.
And the following error is occured;
attempt to create saveOrUpdate event with null entity
I change the scopes of the bean but i did not find the solution.
And i find more solutions for the above exception but my problem is the new values are does not set to currencymaster.
Is there any mistake i done in this context.please guide me..

JSF 2: Ajax not firing in nested composite components

I have the main page with includes testcomponent1 which inturn includes testcomponent2. Ajax call is in testcomponent2. The call is intiated in the javascript but never hits the controller. If the ajax call is moved into testcomponent1, it triggers the call and makes it to the controller. I am including the test code for your reference.
TESTCONTROLLER:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.event.AjaxBehaviorEvent;
#ManagedBean
#ViewScoped
public class TestController implements Serializable {
/** <code>serialVersionUID</code> */
private static final long serialVersionUID = -999213365169849345L;
private int customerkey;
private String customerName;
public void processKeyChange(AjaxBehaviorEvent e){
System.out.println("I am in the Ajax method and my value is: " + customerkey);
}
//GETTERS AND SETTERS
public int getCustomerkey() {
return customerkey;
}
public void setCustomerkey(int customerkey) {
this.customerkey = customerkey;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
testPage.xhtml
<ui:define name="searchCriteria">
<h:form id="orderHeaderForm" prependId="false">
<h:panelGroup id="orderDiv" layout="block">
<com:testcomponent1 id="testComponent1"
customerKey="#{testController.customerkey}"
customerName="#{testController.customerName}"/>
</h:panelGroup>
</h:form>
</ui:define>
</ui:composition>
testcomponent1.xhtml
<cc:interface>
<cc:attribute name="customerKey" type="java.lang.Integer" />
<cc:attribute name="customerName" type="java.lang.String" />
</cc:interface>
<cc:implementation>
<h:panelGroup id="#{cc.clientId}" layout="block">
<com:testcomponent2 id="testcomponent2"
key="#{cc.attrs.customerKey}"
name="#{cc.attrs.customerName}" />
</h:panelGroup>
</cc:implementation>
testcomponent2.xhtml
<cc:implementation>
<h:panelGrid id="#{cc.clientId}" columns="2" border="0" cellspacing="0" cellpadding="0">
<h:outputLabel id="customerid" for="txtcustomerKey"/>
<h:inputText id="txtcustomerKey" value="#{cc.attrs.key}" onchange="alert (document.getElementById(this.id).value)">
<f:ajax event="change" listener="#{testController.processKeyChange}" execute="#this" render="txtCustomerName" />
</h:inputText>
<h:outputLabel id="customerName" for="txtCustomerName"/>
<h:outputText value="#{cc.attrs.name}" id="txtCustomerName" />
</h:panelGrid>
</cc:implementation>
thanks
I think the problem is in <h:panelGrid id="#{cc.clientId}"...>. By default, a composite component is an instance of UINamingContainer, so instead do that, you can set an id with any name and use it. A good practice is avoid EL expressions in id field.
You shouldn't reassign #{cc.clientId} as ID of JSF components, but instead as ID of plain vanilla HTML elements such as <span> or <div>.
<span id="#{cc.clientId}">
or
<div id="#{cc.clientId}">

Checkbox inside ui:repeat not refreshed by Ajax

I work with Mojarra 2.1.3.
When the user click on button "refresh don't work", it refresh the content of the ui:repeat.I expect the checkbox to be checked, just as at the initialization.
What I've found: If I remove h:head in the facelet "refresh don't work" works... Any idea ?
The facelet:
<h:head></h:head>
<h:body>
<h:form id="myForm" >
<h:panelGroup id="panelToRefreshOutsideRepeat">
<ui:repeat value="#{sandbox.columns}" var="column">
<h:panelGroup id="panelToRefreshInsideRepeat">
<h2>composite onlyCheckbox:</h2>
<trc:onlyCheckbox value="#{column.value}" />
<br />
<h2>composite onlyInputText:</h2>
<trc:onlyInputText value="#{column.value}" />
<br />
<br/>
<h:commandButton value="Refresh don't work" >
<f:ajax render="panelToRefreshInsideRepeat" />
</h:commandButton>
<h:commandButton value="Refresh work" >
<f:ajax render=":myForm:panelToRefreshOutsideRepeat" />
</h:commandButton>
</h:panelGroup>
<br/>
</ui:repeat>
</h:panelGroup>
The composite for onlyCheckbox and onlyInputText:
<composite:interface>
<composite:attribute name="value"
type="boolean"/>
</composite:interface>
<composite:implementation>
boolean: <h:selectBooleanCheckbox value="#{cc.attrs.value}" />
<!-- for onlyInputText h:inputText instead of h:selectBooleanCheckbox -->
boolean value: #{cc.attrs.value}
</composite:implementation>
and the backing bean:
#ManagedBean
#RequestScoped
public class Sandbox {
public List<Column> columns = Arrays.asList(new Column(true));
public List<Column> getColumns() {
return columns;
}
public void setColumns(List<Column> columns) {
this.columns = columns;
}
public class Column {
private boolean value;
public Column(boolean value) {
this.value = value;
}
public void setValue(boolean value) {
this.value = value;
}
public boolean getValue() {
return this.value;
}
}
}
I can reproduce your problem even on latest Mojarra 2.1.4. It works fine if the checkbox is not inside a composite. This is a bug in Mojarra's <ui:repeat>. It is totally broken in Mojarra. It works perfectly fine on MyFaces 2.1.3.
You have 2 options:
Replace Mojarra by MyFaces.
Use an UIData component instead of <ui:repeat>, e.g. <h:dataTable>, <t:dataList>, <p:dataList>, etc.

When adding a faces message in JSF my actions aren't performed?

I have the following backing bean:
#ViewScoped
#ManagedBean
public class WeighFamilyBacking2 implements Serializable {
private static final long serialVersionUID = 1L;
private String[] children = new String[] { "Child1", "Child2", "Child3" };
private HashMap<String, Integer> newWeights;
public WeighFamilyBacking2() {
newWeights = new HashMap<String, Integer>();
for (String s : getChildren())
newWeights.put(s, new Integer(0));
}
public void distributeWeightsWithoutMessage(ActionEvent event) {
for (String s : newWeights.keySet()) {
newWeights.put(s, newWeights.get(s) + 1);
}
}
public void distributeWeights(ActionEvent event) {
for (String s : newWeights.keySet()) {
newWeights.put(s, newWeights.get(s) + 1);
}
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage("Succesful", "Weights redistributed."));
}
public HashMap<String, Integer> getNewWeights() {
return newWeights;
}
public List<String> getChildren() {
return Arrays.asList(children);
}
}
... And the following xhtml page:
<!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:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html">
<h:body>
<h:form>
<ui:repeat var="child" value="#{weighFamilyBacking2.children}">
<h:outputText value="#{child}" />
<h:outputText value="#{weighFamilyBacking2.newWeights[child]}" /> -
<h:outputText value="#{weighFamilyBacking2.newWeights[child]}" /> -
<h:inputText id="oz" value="#{weighFamilyBacking2.newWeights[child]}" />
<h:inputText id="lbs"
value="#{weighFamilyBacking2.newWeights[child]}" />
<br />
</ui:repeat>
<h:commandButton
actionListener="#{weighFamilyBacking2.distributeWeights}"
value="Redistribute" />
<h:commandButton
actionListener="#{weighFamilyBacking2.distributeWeightsWithoutMessage}"
value="Redistribute Without Message" />
</h:form>
</h:body>
</html>
This is a simple reproducible test case. When you click on the redistribute without message, things work as expected. When you click on the redistribute button it displays the success message but the input fields are not updated. However, the text output field is updated just one time.
I have tried using immediate=true on both buttons and that doesn't affect this. This is a very simple case, I can't understand why it doesn't work.
I have tried this with all recent versions of Mojarra including 2.1.3.
This is another <ui:repeat> anomaly. I haven't nail down the exact root cause yet so that I can check if this is already reported to the JSF guys and if necessary report it, but I can tell that it works when I replace the <ui:repeat> by a <h:dataTable>.
<h:dataTable var="child" value="#{weighFamilyBacking2.children}">
<h:column>
<h:outputText value="#{child}" />
<h:outputText value="#{weighFamilyBacking2.newWeights[child]}" /> -
<h:outputText value="#{weighFamilyBacking2.newWeights[child]}" /> -
<h:inputText id="oz" value="#{weighFamilyBacking2.newWeights[child]}" />
<h:inputText id="lbs"
value="#{weighFamilyBacking2.newWeights[child]}" />
</h:column>
</h:dataTable>
Maybe a <table> is not semantically correct for you. If this is really undesireable, you might want to check if it works without problems with Tomahawk's <t:dataList>, RichFaces' <rich:dataList>, PrimeFaces' <p:dataList>, etc each which supports rendering the children without additional markup.
Update: I reported it as issue 2157.

Resources