Environment:
PrimeFaces 5.1
Mojarra 2.2.2
Spring 4.0.2
Current Code:
I have the following XHTML page:
<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"
xmlns:c="http://java.sun.com/jsp/jstl/core"
template="/WEB-INF/pages/main/admin.xhtml">
<ui:define name="contentBody">
<p:dataTable widgetVar="weekdayTable"
value="#{weekdayList.countries}"
var="ctr"
styleClass="weekdaysManagementDatatable">
<p:column width="100"
sortBy="#{ctr.desc}"
headerText="#{msg['page.admin.weekday.list.country']}">
<h:outputText value="#{ctr.desc}" />
</p:column>
<c:forEach items="#{weekdayList.weekdays}" var="day">
<c:set var="open" value="#{weekdayList.checkOpen(ctr.id, day.date)}"/>
<p:column width="20" headerText="#{utils.dateToDayWeek(day.date)}">
<h:selectBooleanCheckbox value="#{open}">
<p:ajax listener="#{weekdayList.handleDay(ctr.id, day.id)}" />
</h:selectBooleanCheckbox>
</p:column>
</c:forEach>
</p:dataTable>
</ui:define>
</ui:composition>
The associated bean is the following :
#Named("weekdayList")
#Scope("session")
public class AdminWeekdayListController implements Serializable {
...
public boolean checkOpen(String ctrId, Date date) {
Country ctr = countryDao.getById(ctrId);
List<WeekDay> list = weekdaysMap.get(ctr);
if (list == null) {
return true;
}
WeekDay day = new WeekDay();
day.setDate(date);
for (WeekDay wd : list) {
if (wd.compareTo(day) == 0) {
return wd.isOpen();
}
}
return true;
}
}
I have another bean which maintains the current page to display and the whole the application is made with AJAX. The user is always on the same page and the content is dynamically loaded.
The problem:
When I reload (F5) the page, all seems to work correctly.
But, if I go trought the normal navigation (AJAX loading), I have got a PropertyNotFoundException...
Here is the stack trace:
SEVERE: javax.faces.component.UpdateModelException: javax.el.PropertyNotFoundException: /WEB-INF/pages/main/admin/weekdayList.xhtml #32,47 value="#{open}": /WEB-INF/pages/main/admin/weekdayList.xhtml #29,74 value="#{weekdayList.checkOpen(ctr.id, day.date)}": Property 'checkOpen' not found on type stomac.web.controller.AdminWeekdayListController
at javax.faces.component.UIInput.updateModel(UIInput.java:867)
at javax.faces.component.UIInput.processUpdates(UIInput.java:749)
at org.primefaces.component.api.UIData.process(UIData.java:342)
at org.primefaces.component.api.UIData.processChildren(UIData.java:323)
at org.primefaces.component.api.UIData.processPhase(UIData.java:285)
at org.primefaces.component.api.UIData.processUpdates(UIData.java:271)
at org.primefaces.component.datatable.DataTable.processUpdates(DataTable.java:651)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIForm.processUpdates(UIForm.java:281)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIComponentBase.processUpdates(UIComponentBase.java:1286)
at javax.faces.component.UIViewRoot.processUpdates(UIViewRoot.java:1254)
at com.sun.faces.lifecycle.UpdateModelValuesPhase.execute(UpdateModelValuesPhase.java:78)
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:198)
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:646)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:769)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:585)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:577)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:223)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1125)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:515)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:185)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1059)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:215)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:110)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:497)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:311)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:248)
at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:540)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:610)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:539)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.el.PropertyNotFoundException: /WEB-INF/pages/main/admin/weekdayList.xhtml #32,47 value="#{open}": /WEB-INF/pages/main/admin/weekdayList.xhtml #29,74 value="#{weekdayList.checkOpen(ctr.id, day.date)}": Property 'checkOpen' not found on type stomac.web.controller.AdminWeekdayListController
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:133)
at javax.faces.component.UIInput.updateModel(UIInput.java:832)
... 36 more
Caused by: javax.el.PropertyNotFoundException: /WEB-INF/pages/main/admin/weekdayList.xhtml #29,74 value="#{weekdayList.checkOpen(ctr.id, day.date)}": Property 'checkOpen' not found on type stomac.web.controller.AdminWeekdayListController
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:133)
at org.apache.el.parser.AstIdentifier.setValue(AstIdentifier.java:129)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:260)
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:131)
... 37 more
Caused by: javax.el.PropertyNotFoundException: Property 'checkOpen' not found on type stomac.web.controller.AdminWeekdayListController
at javax.el.BeanELResolver$BeanProperties.get(BeanELResolver.java:245)
at javax.el.BeanELResolver$BeanProperties.access$300(BeanELResolver.java:222)
at javax.el.BeanELResolver.property(BeanELResolver.java:332)
at javax.el.BeanELResolver.getType(BeanELResolver.java:82)
at com.sun.faces.el.DemuxCompositeELResolver._getType(DemuxCompositeELResolver.java:215)
at com.sun.faces.el.DemuxCompositeELResolver.getType(DemuxCompositeELResolver.java:242)
at org.apache.el.parser.AstValue.setValue(AstValue.java:199)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:260)
at com.sun.faces.facelets.el.TagValueExpression.setValue(TagValueExpression.java:131)
... 40 more
But what make me think that the problem does not come from my bean is, if I remove the #{open} from the value of the <h:selectBooleanCheckbox/> it perfectly works (except that the checkboxes are not checked the way I want):
<c:forEach items="#{weekdayList.weekdays}" var="day">
<c:set var="open" value="#{weekdayList.checkOpen(ctr.id, day.date)}"/>
<p:column width="20" headerText="#{utils.dateToDayWeek(day.date)}">
#{open}
<h:selectBooleanCheckbox value="true">
<p:ajax listener="#{weekdayList.handleDay(ctr.id, day.id)}" />
</h:selectBooleanCheckbox>
</p:column>
</c:forEach>
As you can see I keep using #{open} but not inside the value attibute.
Questions:
What did I wrong? Am I missing something? Is there a bug?
Thanks.
So... I have found a "solution", and respond to my own question.
I added fakes getter and setter for the method checkOpen. So the bean looks like this:
#Named("weekdayList")
#Scope("session")
public class AdminWeekdayListController implements Serializable {
...
public boolean getCheckOpen() {
return true;
}
public void setCheckOpen(boolean b) {
}
public boolean checkOpen(String ctrId, Date date) {
Country ctr = countryDao.getById(ctrId);
List<WeekDay> list = weekdaysMap.get(ctr);
if (list == null) {
return true;
}
WeekDay day = new WeekDay();
day.setDate(date);
for (WeekDay wd : list) {
if (wd.compareTo(day) == 0) {
return wd.isOpen();
}
}
return true;
}
}
This way JSF thinks checkOpen is a valid (read/write) attribute, BUT calls checkOpen(String ctrId, Date date). It never calls the getter or the setter...
If someone can explain this, I will be gratefull. And if someone has a better solution, I am listening.
its the LifeCycle of Taghandlers and UI components.
JSTL-Tags in JSF do not represent components and never become part of the component tree once the view has been built.
TagHandlers are responsible for building the tree, "Once they have done their job, they expire". this means, they are available only in the View Building Phase. at the other hand UI-Components run in View Rendering Phase.
Related
I have an OmniFaces <o:validateMultiple> set to two <p:selectOneMenu>. The one dropdown box is for the home, the other for the away team (of a sports game).
The validator checks, if the combination of the two teams already exists in the schedule, if so, the validator method return false. (see table above in the example: combination already exists... the single item in the list)
Now the problem:
The two select boxes are reset to their null values, but in the background, they seem to keep the value.
The UI tells the user "selection has been cleared", but this is not what I or the users expect.
Example
Before: validation:
After validation:
QUESTION:
How can I restore the select boxes' values after validation fail or rather how do I simply keep the values?
I guess resetting the inputs is just JSF specified behavior?? 🤷‍♂️
Is it a problem with PrimeFaces (I guess not)?
Can it be done? If so, how?
PS: I don't think posting the JSF code for the select boxes could help here, but if it could, please leave a comment
JSF should preserve the state of the component tree as defined by the life cycle of the framework.
However, depending on how your JSF page functions you may choose to for example only partially process a page whenever an event occurs. In this case only the processed components will retain their state.
Let's take your use case as an example and first define a view:
<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:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:p="http://primefaces.org/ui" xmlns:o="http://omnifaces.org/ui"
xmlns:of="http://omnifaces.org/functions">
<h:head>
<title>SelectOneMenu Validation</title>
</h:head>
<h:body>
<h:form>
<o:validateMultiple id="validateMatch" components="home away"
validator="#{selectBackingBean.onValidateMatch}"
message="The combination already exists or is invalid!" />
<p:dataList value="#{selectBackingBean.matches}" var="match">
<f:facet name="header">Home / Away</f:facet>
#{match.home} vs #{match.away}
</p:dataList>
<br/><br/>
<p:selectOneMenu id="home" label="Please select home..." value="#{selectBackingBean.selectedHome}">
<f:selectItems value="#{selectBackingBean.teams}" var="team"
itemValue="#{team}" itemLabel="#{team}" />
</p:selectOneMenu>
<p:selectOneMenu id="away" label="Please select away..." value="#{selectBackingBean.selectedAway}">
<f:selectItems value="#{selectBackingBean.teams}" var="team"
itemValue="#{team}" itemLabel="#{team}" />
</p:selectOneMenu>
<h:panelGroup>
<br/>
<h:message for="validateMatch" />
<h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
</h:panelGroup>
<br/><br/>
<p:commandButton value="Save" action="#{selectBackingBean.onSave}" update="#form" />
</h:form>
</h:body>
</html>
Let's also define the backing bean holding the view state and the model:
#Data
#Named
#ViewScoped
public class SelectBackingBean implements Serializable {
private List<String> teams;
private List<Match> matches;
private String selectedHome;
private String selectedAway;
#Data
#AllArgsConstructor
public class Match {
private String home;
private String away;
}
#PostConstruct
private void init() {
teams = Arrays.asList("Malmö FF", "GAIS", "IFK Göteborg", "AIK");
matches = new ArrayList<>(Arrays.asList(new Match("Malmö FF", "AIK")));
}
public boolean onValidateMatch(FacesContext context, List<UIInput> components,
List<String> values) {
return values.get(0) != values.get(1) && !matches.contains(new Match(values.get(0), values.get(1)));
}
public void onSave() {
matches.add(new Match(selectedHome, selectedAway));
}
}
If we now run this we get the following results:
If we keep going and eventually run into a validation error we get the following:
Notice how it constantly retains the state of all components throghout. There are components in OmniFaces and JSF that allow you to control the state of selected parts of the component tree to get away from this behavior.
I'm guessing there is something going on in your code or that you are somehow resetting the backing bean values connected to the components.
I have now been banging my head for a while over a seemingly simple case. I have a Primefaces <p:inputText> (as a cell inside a <p:dataTable>) for which, when the value changes, I would like to get the value in my controller.
I cannot reveal the real code, only showing the vital excerpts of it:
<p:column headerText="Ratkaisu">
<p:inputText name="inpres" id="inpres" value="#{entity.resolution}">
<p:ajax event="change" immediate="true" execute="#this inpres" render="#this" listener="#{mainViewController.updateEntity}"/>
<f:param value="#{entity}" name="entity"/>
</p:inputText>
</p:column>
The "backing bean" is my mainViewController that is #ViewScoped
The method receiving the ajax data inside the controller:
public void updateEntity(AjaxBehaviorEvent e) {
// I can get the entity via the parameter list
Entity entity = (Entity)FacesContext.getCurrentInstance()
.getExternalContext().getRequestMap().get(entity);
// The value below is always null
Object value = ((UIInput)e.getSource()).getValue();
....
}
Every time the text changes (enter of focus out of the text field), I get an ajax request to my method. Every time, I find the correct Entity from the request map, so the <f:param>-part seems to work.
So my question (or maybe questions) is, that is it possible for me to get read the newly entered value of the InputText in the controller, via the AjaxBehaviorEvent or do I have to try something else (maybe setting the value of the inputText to the f:param)? Also checked that I did not find any values in the requestMap at the server side either...
I also checked what Chrome inspector is posting via ajax requests. I did not find any evidence that the text that I enter in the <p:inputText> would be sent to the server.
Could anyone enlight or give me some pointers on this issue?
EDIT:
As #melloware pointed out, I made the client side example as minimal as possible, still fully running:
<!DOCTYPE html>
<html 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">
<f:view>
<h:head>
</h:head>
<h:body>
<h:form id="someid">
</h:form>
<p:inputText id="inpresx">
<p:ajax event="change" process="#this"
listener="#{mainViewController.doStuff}" />
</p:inputText>
</h:body>
</f:view>
</html>
for the MainViewController, the doStuff part:
public void doStuff(AjaxBehaviorEvent e) {
Object val = ((UIInput)e.getSource()).getValue();
// val is still null!
}
Even if the program now is very minimal, the value for the inputtext does not seem to be sent over to the server side. A strange thing IMO, is that i need those h:form-tags in order for the example to fire ajax requests.
Here is what the xhr POST request params look like when i feed TestingTesting to the inputtext and press enter (using Chrome Inspector):
javax.faces.partial.ajax: true
javax.faces.source: inpresx
javax.faces.partial.execute: inpresx
javax.faces.behavior.event: change
javax.faces.partial.event: change
someid: someid
javax.faces.ViewState: -4351418361364308383:4652862949343904732
From the above data being sent, there seems like no text data from the inputtext is communicated over to the server side. What may I have done wrong?
EDIT 2:
tldr;
The actual solution to the problem for me was to wrap my table inside a form. The ajax tag won't work if there is no form tag that WRAPS it at some level.
Here is my fully working example. Whatever I type in the inputtext like "hello" comes out the in System.out.println.
<!DOCTYPE html>
<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:p="http://primefaces.org/ui"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>PrimeFaces Test</title>
</h:head>
<h:body>
<h:form id="frmTest">
<p:inputText id="inpresx" value="#{testView.testString}" >
<p:ajax event="change" process="#this" listener="#{testView.doStuff}" />
</p:inputText>
</h:form>
</h:body>
</html>
Java:
package org.primefaces.test;
import javax.faces.event.AjaxBehaviorEvent;
import javax.faces.component.UIInput;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ViewScoped;
#ManagedBean(name = "testView")
#ViewScoped
public class TestView implements Serializable {
private String testString;
public void doStuff(AjaxBehaviorEvent e) {
Object val = ((UIInput)e.getSource()).getValue();
System.out.println("Value = " + val);
}
public String getTestString() {
return testString;
}
public void setTestString(String testString) {
this.testString = testString;
}
}
And my Chrome Output:
javax.faces.partial.ajax: true
javax.faces.source: frmTest:inpresx
javax.faces.partial.execute: frmTest:inpresx
javax.faces.behavior.event: change
javax.faces.partial.event: change
frmTest: frmTest
frmTest:inpresx: stackoverflow
javax.faces.ViewState: 9209410033241115450:-5298602114910206430
I have trouble understanding the operation of the #ApplicationScope when the request is made by Ajax.
I created a small example to facilitate understanding. Where I have:
A slider changes a variable called sliderValue in a ManagedBean of #ApplicationScope, the value is set by Ajax.
A poll which is updating the panelGrid to always get the updated value of sliderValue, the update is done by Ajax.
Theoretically all users from accessing this page, should have the same value for sliderValue, and if a user changes the value of the slider, all others should receive the change, right?
But this does not occur. Apparently, when the update is made via Ajax, he is behaving like a ManagedBean of #SessionScope.
When I change the value of the sliderValue, it is being changed in ManagedBean correctly, but other users do not receive the update via the update performed bypoll.
I can only update the sliderValuevalue if I give a REFRESH in the browser and do a full REFRESH the page.
Has anyone experienced similar problem?
index.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:p="http://primefaces.org/ui">
<h:head>
<title>Menu</title>
</h:head>
<h:body>
<h:form prependId="false" id="form1" >
<h:panelGrid id="panelGrid1" columns="1" style="margin-bottom: 10px">
<p:inputText id="txt1" value="#{menuManagedBean.sliderValue}" />
<p:slider id="slider1" for="txt1" >
<p:ajax event="slideEnd" process="txt1" />
</p:slider>
</h:panelGrid>
<p:poll id="poll1" widgetVar="varPool1" async="true" autoStart="true" interval="2" update="panelGrid1" />
</h:form>
</h:body>
</html>
MenuManagedBean.java
import java.io.Serializable;
import java.util.Date;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
#ApplicationScoped
#Named(value = "menuManagedBean")
public class MenuManagedBean implements Serializable {
private int sliderValue;
public MenuManagedBean() {
}
public int getSliderValue() {
System.out.println(new Date() + " - get: " + sliderValue);
return sliderValue;
}
public void setSliderValue(int sliderValue) {
this.sliderValue = sliderValue;
System.out.println(new Date() + " - set: " + sliderValue);
}
}
The <p:poll> submits/processes by default the entire form as in <p:poll process="#form">. Including the current slider value. You should have noticed it by an unnecessary set method call. Every open view submits its own current slider value during the poll. That's why every open view only gets to see its own slider value (leaving race conditions outside consideration when there are "many" open views).
Tell <p:poll> to only process itself, not the entire form.
<p:poll process="#this" ... />
Unrelated to the concrete problem: don't use prependId="false" ever. Get rid of it.
I am having trouble with implementing two SelectOneMenu controls, where data in the second one depends on the selection made in the first. This example on the primeFaces showcase is almost the same as what I want to implement: http://www.primefaces.org/showcase-labs/ui/pprSelect.jsf
except that I have to get the data from a database.
The above example is working correctly in the same project. I am using NetBeans 7.0 with GlassFish 3.1 and PrimeFaces 3.0.M2, the latest drop (20th June 2011).
The source code of the JSF page and the managed bean is attached.
<?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:p="http://primefaces.prime.com.tr/ui"
xmlns:f="http://java.sun.com/jsf/core">
<h:head><title>Facelet Title</title></h:head>
<h:body>
<p:log />
<center>
<h:form>
<h:outputText value="State: "/>
<p:selectOneMenu id="selectState" value="#{stateCityBean.selectedStateArray}">
<f:selectItem itemLabel="Select Any" itemValue="Empty String"/>
<p:ajax update="selectCity" listener="#{stateCityBean.updateCityMap}"/>
<f:selectItems value="#{stateCityBean.stateMap}" />
</p:selectOneMenu>
<p></p>
<h:outputText value="City: "/>
<p:selectOneMenu id="selectCity" value="#{stateCityBean.selectedCityArray}">
<f:selectItem itemLabel="Select Any" itemValue="Empty String"/>
<f:selectItems value="#{stateCityBean.cityMap}"/>
</p:selectOneMenu>
</h:form>
</center>
</h:body>
StateCityBean.java
package com.xyz.mbeans;
import com.iwizability.priceinfo.dao.*;
import com.iwizability.priceinfo.pojo.*;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
import javax.faces.context.Flash;
import javax.faces.event.ValueChangeEvent;
#ManagedBean
#SessionScoped
public class StateCityBean {
private String selectedStateArray;
private Map<String, State> StateMap;
private Map<String, City> CityMap;
private String selectedCityArray;
public StateCityBean() {
System.out.println("Inside.............. ");
StateMap = new LinkedHashMap<String, State>();
CityMap = new LinkedHashMap<String, City>();
}
public String getSelectedStateArray() {return selectedStateArray;}
public void setSelectedStateArray(String selectedStateArray) {this.selectedStateArray = selectedStateArray;}
public Map<String, State> getStateMap() {
StateDaoImpl stateObj = new StateDaoImpl();
StateMap = stateObj.getState();
return StateMap;
}
public void setStateMap(Map<String, State> stateArray) {this.StateMap = stateArray;}
public String getSelectedCityArray() {return selectedCityArray;}
public void setSelectedCityArray(String selectedCityArray) {this.selectedCityArray = selectedCityArray;}
public Map<String, City> getCityMap() {
CityDaoImpl cityObj = new CityDaoImpl();
int stateId = 0;
if (selectedStateArray != null && !selectedStateArray.equals("")) {
stateId = StateMap.get(selectedStateArray).getId();
}
CityMap = cityObj.getCity(stateId);
return CityMap;
}
public void setCityMap(Map<String, City> CityArray) {
this.CityMap = CityArray;
}
public void updateCityMap() {
CityDaoImpl cityObj = new CityDaoImpl();
int stateId = 0;
if (selectedStateArray != null && !selectedStateArray.equals("")) {
stateId = StateMap.get(selectedStateArray).getId();
this.CityMap = cityObj.getCity(stateId);
}
}
}
On debugging, I can see that the updateCityMap method is invoked but the SelectedStateArray variable is null. Even force changing the value of bound CityMap variable does not update the selectCity drop down.
As you would have guessed, I am new to JSF, but the problem is compounded by the fact that I am using a still in development version of the tag library...
I created a demo for the exact same situation you describe in your project. I have a state and city <p:selectOneMenu/> elements on my page. You select a state, and the cities update. If a different state is selected, the city is erased since it may not exist in the state.
The difference is that I use <p:ajax event="change" update="cities, cs"/> to update the elements, and an actionListener to update the city if the state is different.
<p:selectOneMenu id="states" value="#{dataBean.selectedState}"
valueChangeListener="#{dataBean.stateChangeListener(event)}"
style="width: 150px;">
<f:selectItem itemLabel="" itemValue=""/>
<f:selectItems value="#{dataBean.states}"/>
<p:ajax event="change" update="cities, cs"/>
</p:selectOneMenu>
<h:outputLabel value="City:" for="cities"/>
<p:selectOneMenu id="cities"
value="#{dataBean.selectedCity}"
style="width: 150px;">
<f:selectItem itemLabel="" itemValue=""/>
<f:selectItems value="#{dataBean.cities}"/>
<p:ajax event="change" update="cs" />
</p:selectOneMenu>
The whole project and demo code can be found on my blog. I saw this post and decided to post my project. [blog]: http://javaevangelist.blogspot.com/2012/07/primefaces-ajax-enabled.html
Primefaces is trying something diffent. I dont know why. First of all you must know these releases are not stable.
When you analys code with firebug you will shove this.
Lets assume two combo who has ids countries and cities
when you changed the first combo cities update correcty but cities combo' id change to cities_input they add _input prefix. When I analys primefaces source code. Thereare codes something like traverse tree if visited change id by adding _input or _panel. So if you change the combo in second time. Everything work perfect except you said update cities but there is no component who has id cities becouse it has new id cities_input. So your ajax does not work correctly. But they correct this bug in 3.0m4 or after releases.
This is the problem. Another example of this problem it is bug someone open jira for this.
Ä°f you are using login with spring security j_username, j_password change to j_username_input j_password_input. So this breaks the standart and code does not work in second ajax requests.
Hope this helps..
pay attention to lionhearts words. Primefaces namespaces changed in 3.m4 in pages use this.
xmlns:p="http://primefaces.org/ui"
I did a state -> city select in my jsf project the same way you did. The only differences I found are:
my p:ajax has a change event: <p:ajax event="change" update="city" listener="#{contatoMB.filterCities}" />.
my p:ajax comes last after the f:selectItems but this shouldn't be the problem.
my f:selectItems list in the city select is List<javax.faces.model.SelectItem> and not a Map<String, City>, have you tried using SelectItems instead of your map?
my form has an id and prependId false <h:form id="contact-form" prependId="false">, again, this shouldn't be the problem.
my h:selectOneMenu is inside a p:panel, some PrimeFaces components behave in a strange way when inside or outside some other components.
If none of this works maybe the problem is the PrimeFaces version you're using. My PrimeFaces version is 2.2.1.
I use PrimeFaces 3.0.M4 with namespaces:
<html 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">
It seems to work fine.
Maybe its the primefaces version but your update method in your bean looks pretty complicated.
Why not retrieve the Object of the selection from the ajax event?, this way you don't need to define that variable.
public void update(AjaxBehaviorEvent event)
{
Object selectOneMenuObject = ((UIOutput)event.getSource()).getValue();
//String selectedStateArray = (String)((UIOutput)event...
//update temporary collection of second SelectOneMenu
}
Don't know if that will help you, but thats just the way I do it.
you should just add
event="change"
to p:ajax
The problem is, that if a property is changed during an f:ajax request and a binded panelGroup should be newly created depending on that changed value, the old value is used.
This code will explain the problem.
Here is the backingbean TestBean:
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
public String getLast() {
return last;
}
public void setLast(String last) {
this.last = last;
}
public String getName(){
return first+" "+last;
}
public void setDynamicPanel(HtmlPanelGroup panel){ }
public HtmlPanelGroup getDynamicPanel(){
Application app = FacesContext.getCurrentInstance().getApplication();
HtmlPanelGroup component = (HtmlPanelGroup)app.createComponent(HtmlPanelGroup.COMPONENT_TYPE);
HtmlOutputLabel label1 = (HtmlOutputLabel)app.createComponent(HtmlOutputLabel.COMPONENT_TYPE);
label1.setValue(" --> "+getFirst()+" "+getLast());
component.getChildren().add(label1);
return component;
}
and now the jsf/facelet code:
<h:form id="form">
<h:panelGrid columns="1">
<h:inputText id="first" value="#{testBean.first}" />
<h:inputText id="last" value="#{testBean.last}" />
<h:commandButton value="Show">
<f:ajax execute="first last" render="name dyn" />
</h:commandButton>
</h:panelGrid>
<h:outputText id="name" value="#{testBean.name}" />
<h:panelGroup id="dyn" binding="#{testBean.dynamicPanel}" />
</h:form>
After the page was initially loaded the outputText and panelGroup shows both "null" as first and last. But after the button is pressed, the outputText is updated well, but the the panelgroup shows again only "null". This is due to the problem, that the "binded method" dynamicPanel is executed before the update of the first and last properties.
how can workaround this behaviour or what is wrong with my code?
If you add the attribute immediate="true" to your input elements, the values will be applied during the "Apply Request Values" phase, and hence be present before your action executes. You may or may not need the immediate attribute set to true on the commandButton as well.