I have create a test project with JSF2.0 and richfaces. I am trying to plot chart. Now, I got value from database to bean and to datatable. Now when I wanted to pass this value to javascript varible and found this answer from The BalusC very userful. It works fine but the value that javascript variable gets after oncomplete="jsonDemo('#{kpilist.json}')". i.e. the value of #{kpilist.json} is not up-to-date it's last one.
I have printed the value of #{kpilist.json}. If it's printed afer datatabe the value is current. If it's printed before datatable it's last value. Any way since oncomplete attribute of a4j:ajax executes after eveything is completed why doesn't #{kpilist.json} show latest value? What is the order of execution of various listener and oncomplete attributes of richfaces and jsf component?
My Managed Bean:
#ManagedBean(name = "kpilist")
#ViewScoped
public class KPIListController implements Serializable {
private static final long serialVersionUID = 1L;
boolean state = true;
String selectedKPIType;
String selectKPITime = "D";
boolean renderDatatable;
String json;
public String getJson() {
return json;
}
public boolean isRenderDatatable() {
return renderDatatable;
}
public void setRenderDatatable(boolean renderDatatable) {
this.renderDatatable = renderDatatable;
}
public boolean isState() {
return state;
}
public List<String> showViewList() {
Logger.getLogger(KPIListController.class.getName()).warning("Show view List:");
KPIDAO kpiDAO = new KPIDAO();
try {
Logger.getLogger(KPIListController.class.getName()).info("Into show view List ---select One");
return kpiDAO.showViewList(selectKPITime);
} catch (SQLException ex) {
ex.printStackTrace();
Logger.getLogger(KPIListController.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
public void setState(boolean state) {
this.state = state;
}
public String getSelectedKPIType() {
return selectedKPIType;
}
public void setSelectedKPIType(String selectedKPIType) {
this.selectedKPIType = selectedKPIType;
}
public String getSelectKPITime() {
return selectKPITime;
}
public void setSelectKPITime(String selectKPITime) {
this.selectKPITime = selectKPITime;
}
public List<KPI> getKPI() {
Logger.getLogger(KPIListController.class.getName()).warning("Get KPI Values:");
KPIDAO kpiDAO = new KPIDAO();
List<KPI> kpiList = new ArrayList<KPI>();
try {
kpiList = kpiDAO.getKPI(selectedKPIType);
Logger.getLogger(KPIListController.class.getName()).warning("KPI List:"+kpiList.size());
} catch (SQLException ex) {
ex.printStackTrace();
return null;
}
Gson gson = new Gson();
json= gson.toJson(kpiList);
return kpiList;
}
public void resetFormValues() {
Logger.getLogger(KPIListController.class.getName()).warning("Reset form:");
selectedKPIType = "--";
}
}
My View:
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:c="http://java.sun.com/jsp/jstl/core">
<h:head>
</h:head>
<h:body>
<ui:composition template="/contentTemplate.xhtml">
<ui:define name="windowTitle">KPI Index</ui:define>
<ui:define name="content" >
<h:outputScript name="js/graphics/jquery.js"/>
<h:outputStylesheet name="/css/jquery-ui-1.8.22.custom.css"/>
<h:outputScript name="js/graphics/jquery-ui-1.8.22.custom.min.js"/>
<h:outputScript name="js/OpenLayers/OpenLayers.js"/>
<h:outputScript name="js/graphics/raphael-min.js"/>
<h:outputScript name="js/graphics/canvg.js"/>
<h:outputScript name="js/graphics/paths.js"/>
<h:outputScript name="js/graphics/draw.js"/>
<h:form id="ins_sel_form">
<h:outputText value="KPI TIME FRAME"/>
<h:selectOneRadio value="#{kpilist.selectKPITime}" >
<f:selectItem itemLabel="DAILY" itemValue="D" />
<f:selectItem itemLabel="WEEKLY" itemValue="W" />
<f:selectItem itemLabel="LAST WEEK" itemValue="LW" />
<a4j:ajax event="change" render="ins_sel_form:selectOnemenu dataPnl" listener="#{kpilist.resetFormValues()}" />
</h:selectOneRadio>
<h:outputText value="Major KPI Type"/>
<h:selectOneMenu id="selectOnemenu" value="#{kpilist.selectedKPIType}" >
<f:selectItem itemValue="--" itemLabel="--"></f:selectItem>
<f:selectItems itemValue="#{item.toString()}" var="item" itemLabel="#{item.toString()}" value="#{kpilist.showViewList()}"/>
<a4j:ajax event="change" render="dataPnl" oncomplete="jsonDemo('#{kpilist.json}')" />
</h:selectOneMenu>
<h:outputText value="Show / Hide Map"/>
</h:form>
<rich:panel id ="dataPnl">
<rich:dataTable id="kpiValueTable" value="#{kpilist.KPI}" var="kpi" style="width:100%" rows="20" rendered="#{kpilist.selectedKPIType!=null and kpilist.selectedKPIType !='--' }" >
<rich:column>
<f:facet name="header" >
<h:outputText value ="Value"></h:outputText>
</f:facet>
<h:outputText value="#{kpi.KPIValue}"></h:outputText>
</rich:column>
</rich:dataTable>
JSON String : <h:outputText id="json" value ="#{kpilist.json}"/>
<center><rich:dataScroller for="kpiValueTable" rendered="#{kpilist.selectedKPIType!=null and kpilist.selectedKPIType!='--'}"/></center>
</rich:panel>
<rich:panel id="map" style="display: none;">
</rich:panel>
</ui:define>
</ui:composition>
</h:body>
</html>
Javascript:
function jsonDemo(jsonString){
console.log("Chart data already retrieved: " + jsonString);
var data = $.parseJSON(jsonString);
$.each(data,function(i,val){
console.log("The value of i: "+i+" The val: "+val.NCELLCLUSTER);
});
}
The EL expression in your oncomplete is evaluated at the moment the HTML/JS code is generated by JSF (thus, on the initial HTTP request). It's not evaluated at the moment the oncomplete is executed in JS as you seem to expect. It's not the webbrowser who evaluates EL expressions, it's the webserver. The oncomplete is by the way just executed after render. With a HTTP traffic debugger and a JS debugger (press F12 in Chrome/IE9/Firebug) you can easily track it.
There are several possibilities to solve this:
Just invoke a $.get() or $.getJSON() in jQuery and do the job in a normal servlet instead, or better, a JAX-RS webservice.
function jsonDemo() {
$.getJSON("servletURL", function(jsonData) {
// ...
});
}
Replace the oncomplete by some <h:outputScript> which you render/update by ajax.
<a4j:ajax ... render="json" />
...
<h:panelGroup id="json">
<h:outputScript rendered="#{not empty bean.json}">jsonDemo(#{bean.json});</h:outputScript>
</h:panelGroup>
Unrelated to the concrete problem, you've there by the way a conceptual mistake as to passing around JSON data. You're stringifying it while passing as argument like so jsonDemo('#{kpilist.json}') and then you're parsing the JSON afterwards using $.parseJSON(). This makes no sense. Remove those singlequotes around the argument like so jsonDemo(#{kpilist.json}) and then you don't need that $.parseJSON() line anymore. The data is then already in JSON format.
Try changing from a4j:ajax to f:ajax
not sure if a4j:ajax works with plain JSF components
Related
I am using #ViewScoped Bean for small CRUD application I have a edit and view page but when I click buttons (edit) it will render edit form. After edit form appears the save button or cancel button does not call the function but renders the whole page. The actionListener's function is not called at all and everthing is initialized. Is something wrong with my bean and page?? I am using JSF 2 with richfaces and facelet.
//ViewScoped Bean
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.legendMgr.Legend;
import java.io.Serializable;
import java.sql.SQLException;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
import javax.faces.context.FacesContext;
/**
*
* #author kitex
*/
#ManagedBean(name = "legendbean")
#ViewScoped
public class LegendController implements Serializable {
LegendDTO legendDTO;
String selectedLegend;
List<LegendDTO> legendDTOs;
boolean edit;
public List<LegendDTO> getLegendDTOs() {
return legendDTOs;
}
public void setLegendDTOs(List<LegendDTO> legendDTOs) {
this.legendDTOs = legendDTOs;
}
#PostConstruct
void initialiseSession() {
FacesContext.getCurrentInstance().getExternalContext().getSession(true);
}
public LegendController() {
if (!edit) {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
legendDTOs = getLegends();
}
}
public String getSelectedLegend() {
return selectedLegend;
}
public void setSelectedLegend(String selectedLegend) {
this.selectedLegend = selectedLegend;
}
public boolean isEdit() {
return edit;
}
public void setEdit(boolean edit) {
this.edit = edit;
}
public LegendDTO getLegendDTO() {
return legendDTO;
}
public void setLegendDTO(LegendDTO legendDTO) {
this.legendDTO = legendDTO;
}
public void addLegendRange() {
Logger.getLogger(LegendController.class.getName()).warning("List Size " + legendDTO.getList().size());
legendDTO.getList().add(new Legend());
Logger.getLogger(LegendController.class.getName()).warning("List Size " + legendDTO.getList().size());
}
public void removeLegendRange(Legend legend) {
if (legendDTO.getList().size() != 1) {
legendDTO.getList().remove(legend);
}
}
public String saveLegend() {
Logger.getLogger(LegendController.class.getName()).warning("Save Legend Edit" + edit);
LegendDAO dao = new LegendDAO();
if (dao.addLegend(legendDTO, edit)) {
edit = false;
Logger.getLogger(LegendController.class.getName()).warning("Save Legend Edit" + edit);
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Could Not Save Confim if you have already defined Legend " + legendDTO.getLegendName() + "!"));
}
return "";
}
public String cancel() {
edit = false;
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
return "";
}
public List<LegendDTO> getLegends() {
LegendDAO dao = new LegendDAO();
return dao.getLegendDTO();
}
//All function from here are for legend delete
public void deleteLegendType(LegendDTO dto) {
LegendDAO dao = new LegendDAO();
if (dao.deleteLegendType(dto.getLegendName())) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Deleted !"));
} else {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Deleted Error !"));
}
}
//All function from here is to legend edit
public void editLegendType(LegendDTO dto) {
edit = true;
Logger.getLogger(LegendController.class.getName()).warning("DTO : " + dto.legendName);
legendDTO = dto;
LegendDAO dao = new LegendDAO();
Logger.getLogger(LegendController.class.getName()).warning("Edit dto set");
try {
List<Legend> legends = dao.getDetailForEditLegend(dto.getLegendName());
if (legends == null || legends.isEmpty()) {
dto.getList().add(new Legend());
} else {
dto.setList(legends);
}
} catch (SQLException ex) {
Logger.getLogger(LegendController.class.getName()).warning("SQL EXception has occoured");
}
Logger.getLogger(LegendController.class.getName()).warning("In Edit Legend Function The size of list" + dto.getList().size());
}
}
//xhtml code
<?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://java.sun.com/jsf/html"
xmlns:rich="http://richfaces.org/rich"
xmlns:a4j="http://richfaces.org/a4j"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<ui:composition template="/legendTemplate.xhtml">
<ui:define name="windowTitle">Change Legend</ui:define>
<ui:define name="content">
<h:messages globalOnly="true"/>
<rich:panel id="firstPanel">
<h:form id="nis_viewLegend">
<rich:dataTable id="data_tbl" value="#{legendbean.legendDTOs}" var="legendDTOvar" style="width:100%" rendered="#{!legendbean.edit and not empty legendbean.legendDTOs}">
<rich:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:outputText value="#{legendDTOvar.desc}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Legend Type"/>
</f:facet>
<h:outputText value="#{legendDTOvar.legendName}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Legend Type"/>
</f:facet>
<h:outputText value="#{legendDTOvar.legendFor}"/>
</rich:column>
<rich:column>
<a4j:commandLink value="Delete" actionListener="#{legendbean.deleteLegendType(legendDTOvar)}" render=":firstPanel"/>
<h:outputText value="/"/>
<a4j:commandLink value="Edit" actionListener="#{legendbean.editLegendType(legendDTOvar)}" render=":secondPanel :editLegendForm :nis_viewLegend"/>
</rich:column>
</rich:dataTable>
</h:form>
</rich:panel>
<rich:panel id="secondPanel">
<h:form id="editLegendForm" rendered="#{legendbean.edit}">
<h:outputText value="Legend Name"/><br/>
<h:inputText value="#{legendbean.legendDTO.legendName}" readonly="true"/><br/>
<h:outputText value="Description"/><br/>
<h:inputText value="#{legendbean.legendDTO.desc}"/><br/>
<h:outputText value="Legend For"/><br/>
<h:inputText value="#{legendbean.legendDTO.legendFor}"/><br/>
<br/>
<h:outputText value="Range" />
<rich:dataTable id="editDataPnl" value="#{legendbean.legendDTO.list}" var="legend" style="width:100%">
<rich:column>
<f:facet name="header">
<h:outputText value="SN"/>
</f:facet>
<h:inputText value="#{legend.sn}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Description"/>
</f:facet>
<h:inputText value="#{legend.desc}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Lower Range"/>
</f:facet>
<h:inputText value="#{legend.lowerRange}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Upper Range"/>
</f:facet>
<h:inputText value="#{legend.upperRange}"/>
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Color"/>
</f:facet>
<h:inputText value="#{legend.color}"/>
</rich:column>
<rich:column>
<a4j:commandLink value="Add" actionListener="#{legendbean.addLegendRange}" render=":secondPanel"/>
<h:outputText value=" / "/>
<a4j:commandLink value="Remove" actionListener="#{legendbean.removeLegendRange(legend)}" render=":secondPanel"/>
</rich:column>
</rich:dataTable>
<br/>
<center>
<a4j:commandButton value="SAVE" action="#{legendbean.saveLegend()}" render=":firstPanel :secondPanel"/>
<a4j:commandButton value="CANCEL" action="#{legendbean.cancel()}" render=":firstPanel :secondPanel"/>
</center>
</h:form>
</rich:panel>
</ui:define>
</ui:composition>
</h:body>
</html>
In ViewScope, once the view is built, for example form.xhtml, its data will last as long you do not go away from this view. To stay in the same view you should call methods that has return type void (which are usually used in actionListener property) or return null, in case of returning an outcome for navigation.
Method expression
In your case your methods are void but instead of passing it to the action listener you're calling it in the view.
Try changing similar code like this:
<a4j:commandButton value="SAVE" actionListener="#{legendbean.saveLegend()}" render="mainPnl"/>
To this:
<a4j:commandButton value="SAVE" actionListener="#{legendbean.saveLegend}" render="mainPnl"/>
As actionListener property already expects a method expression.
Form inside dataTable
Also I noticed you have a form inside your dataTable. That could lead to strange behavior because your form has an id it will be repeated in the resulting page. For that you should try placing the form outside the dataTable.
Even better you could have only one form enclosing the entire code as nested forms are invalid HTML code.
I would suggest you check your legendTemplate.xhtml against nested forms too.
Bean construction
In order to initialize your bean state it is recommended to use a #PostContruct method instead of the bean constructor.
Try changing from this:
public LegendController() {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
}
To this:
#PostConstruct
public void reset() {
legendDTO = new LegendDTO();
legendDTO.getList().add(new Legend());
}
And delete your constructor.
Your bean data should be kept as long as you're in the same view (aka .xhtml page).
I hope it helps.
I am having the same problem. I don't think the answers above really address the question. He is not having a problem as a result of submitting any forms - pressing those buttons themselves results in the whole page re-rendering, which means that the reference to the view state is already gone before the button is pressed. This is what I am observing as well. For me, it only happens when there are a lot of large search results, and simply re-submitting the same search I just did re-renders the page (without executing the search). I believe the problem has to do with a limit on the amount of data that can be passed to the server in a form: in view scope, all of the data is serialized and passed around in one long hidden value as a value in the form. If that value is too long the server won't accept it and, therefore, will not remember the previous state.
I know this is not definitive, but this is only thread out there on this problem I can find so I hope it helps shed light for others or inspires better information. If you have something more definitive please let us know.
Edit: I am convinced now that this was the problem. My model bean had a reference to a file blob. Once I replaced that reference with a boolean (only needed to know if it existed) the problem went away. Try passing around references to your DTOs/DAOs instead of the objects themselves, or mark them as "transient" where you don't need them to persist. Or, if possible, lighten the objects as I did.
Did you try to return null for functions saveLegend() and cancel() instead of returning empty string?
public String saveLegend() {
---------
return null;
}
public String cancel() {
----------
return null;
}
Return can also be void for ajax request. But if I remember it correctly for richfaces returning null is the only solution. Give it a try. :)
The cancel() works as it reinitialize the bean.
As answered by Bento you cannot pass values using actionListener. Use action instead.
Further Reading:
JSF 2 ViewScope questions
JSF2 Action parameter
Differences between action and actionListener
I have a simple form that get code then display his libelle, I added a validator bean that check if the code exist.
My problem is I can't display the error message whith when the code doesn't exist.
Here is the code:
test.xhtml
<!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://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head><title>Test</title>
</h:head>
<body class="bodyMain">
<h:form>
<h:panelGrid columns="3">
<h:outputText value="Code: " />
<h:inputText id="code" value="#{myBean.code}"
validator="#{myBean.validateCode}">
<f:ajax execute="#this" render="libelle" listener="#{myBean.setLibelle()}"/>
</h:inputText>
<h:message for="code" style="color:red"/>
</h:panelGrid>
<h:panelGrid id="libelle" columns="2">
<h:outputText value="Libelle: " />
<h:outputText value="#{myBean.libelle}" />
</h:panelGrid>
</h:form>
</body>
</html>
MyBean.java
#ManagedBean
#ViewScoped
public class MyBean implements java.io.Serializable{
private static final long serialVersionUID = 1L;
private String code="";
private String libelle="";
public String getCode() {
return this.code;
}
public void setCode(String code) {
this.code=code;
}
public String getLibelle() {
return this.libelle;
}
public void setLibelle(String libelle) {
this.libelle=libelle;
}
public void setLibelle() {
if (code.compareTo("1")==0)
libelle="One";
else
libelle="";
}
public void validateCode(FacesContext context, UIComponent toValidate, Object value) throws ValidatorException {
String code = (String)value;
if (code.compareTo("1") != 0) {
FacesMessage message = new FacesMessage("Code doesn't exist");
throw new ValidatorException(message);
}
}
}
Thank you for your help to resolve this problem
You're not updating the <h:message> component by the <f:ajax>. You need to give the <h:message> an id and include it in the <f:ajax render>.
<h:inputText id="code" value="#{myBean.code}" validator="#{myBean.validateCode}">
<f:ajax execute="#this" render="libelle codeMessage" listener="#{myBean.setLibelle()}"/>
</h:inputText>
<h:message id="codeMessage" for="code" style="color:red"/>
Unrelated to the concrete problem, don't initialize properties to empty strings. Let them by default null. Also, comparing objects should be done with equals() method, not with compareTo(). Finally, using a dropdown list with all available values instead of an input field would be more user friendly.
I have read there, but i can't take edited value from primefaces datatable cellEditor, it gives me unedited value. i am using jpa.
xhtml page:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<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.prime.com.tr/ui"
template="/templates/masterLayout.xhtml">
<ui:define name="windowTitle">
learn
</ui:define>
<ui:define name="content">
<h:form>
<p:dataTable value="#{lesson.lessonValue}" var="l" style="width: 400px">
<p:ajax event="rowEdit" listener="#{lesson.onEditRow}"/>
<p:column headerText="Lessons" style="width: 300px">
<p:cellEditor>
<f:facet name="output">
<h:outputText value="#{l.lessonName}"/>
</f:facet>
<f:facet name="input">
<p:inputText value="#{l.lessonName}" style="width: 100%"/>
</f:facet>
</p:cellEditor>
</p:column>
<p:column headerText="Options">
<p:rowEditor />
</p:column>
</p:dataTable>
</h:form>
</ui:define>
</ui:composition>
lesson.java:
public class lesson implements Serializable {
private String name;
protected EntityLesson[] lessonList;
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
EntityManagerFactory emf = Persistence.createEntityManagerFactory("DefaultPU");
public EntityLesson[] getLessonValue() {
EntityManager em = emf.createEntityManager();
List<EntityLesson> result;
try {
EntityTransaction entr = em.getTransaction();
boolean committed = false;
entr.begin();
try {
Query query = em.createQuery("SELECT l FROM EntityLesson l");
result = query.getResultList();
entr.commit();
committed = true;
lessonList = new EntityLesson[result.size()];
lessonList = result.toArray(lessonList);
} finally {
if (!committed) entr.rollback();
}
} finally {
em.close();
}
return lessonList;
}
public void onEditRow(RowEditEvent event) {
EntityLesson editedLesson = (EntityLesson)event.getObject();//gives me unedited value
............................
............................
}
EntityLesson.java:
#Entity
#Table(name="lessonaaa")
public class EntityLesson implements Serializable {
#Id
#Column(name="Lesson_Id", nullable=false)
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int lessonId;
#Column(name="Lessson", nullable=false, length=65)
private String lessonName;
public int getLessonId() { return lessonId; }
public void setLessonId(int lessonId) { this.lessonId = lessonId; }
public String getLessonName() { return lessonName; }
public void setLesson (String lessonName) { this.lessonName = lessonName; }
}
The problem occurs because of the JSF lifecycle:
When your dataTable is displayed it executes the JPQL to retrieve the list of lessons. After that they are displayed.
Now you edit on entity and hit save, the edited entity in the list has now the new value.
But what happens next, is that the list is fetched another time and then the listener method is executed with the newly fetched enitiy.
You can solve the problem if you store the list of entities in a local attribute in the view bean and fill it in the post construct method (annotated by #PostContruct) and you have to make the view bean #SessionScoped. Then use this list for the datatable.
My problem is similar:
The 'dataTable' contains as 'value' a list of entities:
<p:dataTable id="category" var="category" value="#{categoriesBacking.categoriesListEdit}">
If I select one for edit, the object which is passed to the event contains the previously unmodified value. I observed that this is due to the fact that dataTable's value is a list. As a work-around (to be able to use the component) I added a 'filterBy' to any of the 'column'. If the dataTable will contain only one value, that value will be interpreted correctly by the passed event in the managed bean.
!!! The event's object will be the modified instance.
I also use:
<p:ajax event="rowEdit" update="#this" listener="#{categoriesBacking.onEditRow}" />
instead of dataTable's 'rowEditListener'.
Again, this is just a workaround.
I have almost exactly the same code except instead of using the <p:ajax> tag I am instead using the rowEditListener attribute of dataTable. Try this instead:
<p:dataTable ... rowEditListener="#{lesson.onEditRow}" ... >
...
This kind of problems are generally related to your backing bean. Your "lesson" class needs #ManagedBean (javax.faces.bean.ManagedBean) annotation. Simply add
#ManagedBean (name="YourBeanName")
#ViewScoped
just before public class lesson implements Serializable { line in your lesson.java
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.
I have a JSF page which has a variable number inputText elements containing numeric weights. These are all bound to Weight objects in my backing bean. I'd like to create a single actionListener button which will re-distribute the weights across all the input texts.
I can call the method in the backing bean which distributes the values contained in the weight objects within the backing bean, but for some reason those updated values are not reflected in the InputText elements.
It is my assumption that the values are being put back in the UI elements before I update the values. Just shows my lack of understanding of the JSF lifecycle.
Can someone tell me how I could accomplish this?
Here is the relevant part of the xhtml file. For each child I reference the "newWeight" object key'd by the child object. The newWeight objects are created in the loadFamily method which is bound to the preRenderView event:
<!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:a4j="http://richfaces.org/a4j"
xmlns:rich="http://richfaces.org/rich"
xmlns:h="http://java.sun.com/jsf/html">
<ui:composition template="/templates/layout.xhtml">
<f:metadata>
<f:viewParam name="familyId" required="true"
requiredMessage="familyId is required"
value="#{weighFamilyBacking.familyId}"></f:viewParam>
<f:event type="preRenderView"
listener="#{weighFamilyBacking.loadFamily}" />
</f:metadata>
<ui:define name="title">Weigh Family</ui:define>
<ui:define name="content">
<h:form>
<h2>Previous Weights</h2>
<rich:dataTable value="#{weighFamilyBacking.allWeights}" var="weight"
id="table">
<rich:column>
<f:facet name="header">
<h:outputText value="Child" />
</f:facet>
<h:outputText
value="#{weight.child.firstName} #{weight.child.lastName}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Weight" />
</f:facet>
<h:outputText value="#{weight.weight}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Weigh Time" />
</f:facet>
<h:outputText value="#{weight.weighTime}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Payout Time" />
</f:facet>
<h:outputText value="#{weight.payoutTime}" />
</rich:column>
<rich:column>
<f:facet name="header">
<h:outputText value="Payout Status" />
</f:facet>
<h:outputText value="#{weight.status}" />
</rich:column>
</rich:dataTable>
<h2>New Weights</h2>
<h:panelGroup id="newWeights">
<ul>
<ui:repeat var="child" value="#{weighFamilyBacking.children}">
<li><h:panelGroup layout="block">
<h:outputText value="#{child.firstName}" />
<h:outputLabel value="Lbs" for="lbs">
<h:inputText size="3" id="lbs"
value="#{weighFamilyBacking.newWeights[child].lbs}" />
</h:outputLabel>
<h:outputLabel value="Oz" for="oz">
<h:inputText id="oz" size="3"
value="#{weighFamilyBacking.newWeights[child].oz}" />
</h:outputLabel>
</h:panelGroup></li>
</ui:repeat>
<h:outputLabel value="Donate Only" for="donate">
<h:selectBooleanCheckbox id="donate"
value="#{weighFamilyBacking.donateOnly}" />
</h:outputLabel>
</ul>
</h:panelGroup>
<h:commandButton
actionListener="#{weighFamilyBacking.distributeWeights}"
immediate="true" value="Redistribute" />
<h:commandButton action="cancel" value="Cancel" />
<h:commandButton action="#{weighFamilyBacking.save}" value="Save" />
</h:form>
</ui:define>
</ui:composition>
</html>
Here is the backing bean:
#ViewScoped
#ManagedBean
public class WeighFamilyBacking extends BaseForm implements Serializable {
private static final long serialVersionUID = 3710213437377609887L;
private Integer familyId;
private Boolean donateOnly;
public Boolean getDonateOnly() {
return donateOnly;
}
public void distributeWeights(ActionEvent event) {
Integer oz = 0;
Integer count = 0;
for (WebWeight ww : newWeights.values()) {
System.out.println(ww);
oz += ww.getLbs() * 16;
oz += ww.getOz();
ww.setLbs(123); // Set the values to something to simulate re-distribution for now.
ww.setOz(456);
count++;
}
donateOnly = true;
}
public void setDonateOnly(Boolean donateOnly) {
this.donateOnly = donateOnly;
}
private Family family;
private HashMap<Child, WebWeight> newWeights;
public HashMap<Child, WebWeight> getNewWeights() {
return newWeights;
}
public void setNewWeights(HashMap<Child, WebWeight> newWeights) {
this.newWeights = newWeights;
}
public WeighFamilyBacking() {
newWeights = new HashMap<Child, WebWeight>();
}
public List<Weight> getAllWeights() {
List<Weight> weights = new ArrayList<Weight>();
for (Child c : getFamily().getChildrenAsList()) {
for (Weight w : c.getWeightsAsList())
weights.add(w);
}
Collections.sort(weights, new Comparator<Weight>() {
#Override
public int compare(Weight arg0, Weight arg1) {
if (arg0.getWeighTime() == null)
return -1;
Integer date = arg0.getWeighTime().compareTo(
arg1.getWeighTime());
if (date == 0)
return arg0.getChild().getFirstName()
.compareTo(arg1.getChild().getFirstName());
return date;
}
});
return weights;
}
#PostConstruct
public void init() {
}
public void loadFamily() {
if (family == null) {
setFamily(hcbbService.findFamilyById(getFamilyId()));
for (Child c : family.getChildrenAsList()) {
WebWeight w = new WebWeight();
newWeights.put(c, w);
}
}
}
public void setFamilyId(Integer id) {
this.familyId = id;
}
public Integer getFamilyId() {
return this.familyId;
}
public Family getFamily() {
return family;
}
public void setFamily(Family f) {
this.family = f;
}
public List<Child> getChildren() {
List<Child> children = getFamily().getChildrenAsList();
Collections.sort(children);
return children;
}
public String cancel() {
return "cancel";
}
public String save() {
for (Child c : newWeights.keySet()) {
WebWeight ww = newWeights.get(c);
Weight w = new Weight();
w.setWeighTime(new Date());
Double weight = (ww.getLbs() * 16.0 + ww.getOz()) / 16.0;
w.setWeight(weight);
w.setStatus(WeightStatus.AWAITING_PAYOUT);
c.getWeights().add(w);
}
hcbbService.updateRegistration(family);
return "success";
}
}
The goal is to allow us to put weights in a single child and then have it evenly distribute the values across all "newWeight" objects. Right now I would expect that all my UI elements linked to the NewWeight objects would be zero after I click redistribute (since that method is getting called, and the values are being reset), but they aren't.
Additional Info
My adjustment of the WebWeight objects is happening at the INVOKE_APPLICATION phase as expected. The objects have the correct values before Render response.. but not in the rendered components?
From Log:
BEFORE INVOKE_APPLICATION 5
net.halo3.hcbb.registration.WebWeight#2f6cd09f
net.halo3.hcbb.registration.WebWeight#10f48f0c
net.halo3.hcbb.registration.WebWeight#27db6586
AFTER INVOKE_APPLICATION 5
BEFORE RENDER_RESPONSE 6
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight#2f6cd09f
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight#10f48f0c
oz=456 lbs=123 object=net.halo3.hcbb.registration.WebWeight#27db6586
AFTER RENDER_RESPONSE 6
More Info
Here is the dump from logs, where I log getLbs and getOz.. You can see that getLbs or getOz on the WebWeight object is not called during the Render phase?
- BEFORE INVOKE_APPLICATION 5
net.halo3.hcbb.registration.WebWeight#12d58dfe
net.halo3.hcbb.registration.WebWeight#12d58dfe getLbz: 0
net.halo3.hcbb.registration.WebWeight#12d58dfe getOz:0
net.halo3.hcbb.registration.WebWeight#25d285b
net.halo3.hcbb.registration.WebWeight#25d285b getLbz: 0
net.halo3.hcbb.registration.WebWeight#25d285b getOz:0
net.halo3.hcbb.registration.WebWeight#6c317dc9
net.halo3.hcbb.registration.WebWeight#6c317dc9 getLbz: 0
net.halo3.hcbb.registration.WebWeight#6c317dc9 getOz:0
- AFTER INVOKE_APPLICATION 5
- BEFORE RENDER_RESPONSE 6
- AFTER RENDER_RESPONSE 6
The thing is that you have set the immediate attribute on the command button.
This will cause your action listener to be called during the APPLY_REQUEST phase, but additionally UPDATE_MODEL will not be invoked and when the view is rendered again it will render the values that were still in the input components (components are stateful in JSF).
See this for some more elaborate information: JSF commandButton with immediate="true"
As you don't have any validators attached to your input components, the simplest solution in your case might be to remove the immediate attribute.
(offtopic, but I noticed your backing bean is rather randomly ordered which makes it a bit difficult to read. In Java/JSF you typically put the instance variables at the very beginning, then the constructor, post construct method, preRenderView event handler etc so the class reads as a story top to bottom)
It turned out that the problem was some of the xhtml. When I cleaned it all out and started simple it started working. I'm thinking maybe the outputLabels or the panelGroups.